Permalink
Fetching contributors…
Cannot retrieve contributors at this time
159 lines (95 sloc) 25 KB
title ms.date helpviewer_keywords ms.assetid
WPF and Win32 Interoperation
03/30/2017
hosting WPF content in Win32 window [WPF]
HWND interop [WPF]
Win32 code [WPF], WPF interoperation
interoperability [WPF], Win32
0ffbde0d-701d-45a3-a6fa-dd71f4d9772e

WPF and Win32 Interoperation

This topic provides an overview of how to interoperate [!INCLUDETLA2#tla_winclient] and [!INCLUDETLA2#tla_win32] code. [!INCLUDETLA#tla_winclient] provides a rich environment for creating applications. However, when you have a substantial investment in [!INCLUDETLA#tla_win32] code, it might be more effective to reuse some of that code.

WPF and Win32 Interoperation Basics

There are two basic techniques for interoperation between [!INCLUDETLA2#tla_winclient] and [!INCLUDETLA2#tla_win32] code.

Each of these techniques is conceptually introduced in this topic. For a more code-oriented illustration of hosting [!INCLUDETLA2#tla_winclient] in [!INCLUDETLA2#tla_win32], see Walkthrough: Hosting WPF Content in Win32. For a more code-oriented illustration of hosting [!INCLUDETLA2#tla_win32] in [!INCLUDETLA2#tla_winclient], see Walkthrough: Hosting a Win32 Control in WPF.

WPF Interoperation Projects

[!INCLUDETLA2#tla_winclient] [!INCLUDETLA2#tla_api#plural] are managed code, but most existing [!INCLUDETLA2#tla_win32] programs are written in unmanaged [!INCLUDETLA2#tla_cpp]. You cannot call [!INCLUDETLA2#tla_winclient] [!INCLUDETLA2#tla_api#plural] from a true unmanaged program. However, by using the /clr option with the [!INCLUDETLA#tla_visualcpp] compiler, you can create a mixed managed-unmanaged program where you can seamlessly mix managed and unmanaged [!INCLUDETLA2#tla_api] calls.

One project-level complication is that you cannot compile [!INCLUDETLA#tla_xaml] files into a [!INCLUDETLA2#tla_cpp] project. There are several project division techniques to compensate for this.

Use whatever approach works best for you.

[!NOTE] If you have not used [!INCLUDETLA#tla_cppcli] before, you might notice some "new" keywords such as gcnew and nullptr in the interoperation code examples. These keywords supersede the older double-underscore syntax (__gc) and provide a more natural syntax for managed code in [!INCLUDETLA2#tla_cpp]. To learn more about the [!INCLUDETLA#tla_cppcli] managed features, see Component Extensions for Runtime Platforms and Hello, C++/CLI.

How WPF Uses Hwnds

To make the most of [!INCLUDETLA2#tla_winclient] "HWND interop", you need to understand how [!INCLUDETLA2#tla_winclient] uses HWNDs. For any HWND, you cannot mix [!INCLUDETLA2#tla_winclient] rendering with [!INCLUDETLA2#tla_dx] rendering or [!INCLUDETLA2#tla_gdi] / [!INCLUDETLA2#tla_gdiplus] rendering. This has a number of implications. Primarily, in order to mix these rendering models at all, you must create an interoperation solution, and use designated segments of interoperation for each rendering model that you choose to use. Also, the rendering behavior creates an "airspace" restriction for what your interoperation solution can accomplish. The "airspace" concept is explained in greater detail in the topic Technology Regions Overview.

All [!INCLUDETLA2#tla_winclient] elements on the screen are ultimately backed by a HWND. When you create a [!INCLUDETLA2#tla_winclient] xref:System.Windows.Window, [!INCLUDETLA2#tla_winclient] creates a top-level HWND, and uses an xref:System.Windows.Interop.HwndSource to put the xref:System.Windows.Window and its [!INCLUDETLA2#tla_winclient] content inside the HWND. The rest of your [!INCLUDETLA2#tla_winclient] content in the application shares that singular HWND. An exception is menus, combo box drop downs, and other pop-ups. These elements create their own top-level window, which is why a [!INCLUDETLA2#tla_winclient] menu can potentially go past the edge of the window HWND that contains it. When you use xref:System.Windows.Interop.HwndHost to put an HWND inside [!INCLUDETLA2#tla_winclient], [!INCLUDETLA2#tla_winclient] informs [!INCLUDETLA2#tla_win32] how to position the new child HWND relative to the [!INCLUDETLA2#tla_winclient] xref:System.Windows.Window HWND.

A related concept to HWND is transparency within and between each HWND. This is also discussed in the topic Technology Regions Overview.

Hosting WPF Content in a Microsoft Win32 Window

The key to hosting a [!INCLUDETLA2#tla_winclient] on a [!INCLUDETLA2#tla_win32] window is the xref:System.Windows.Interop.HwndSource class. This class wraps the [!INCLUDETLA2#tla_winclient] content in a [!INCLUDETLA2#tla_win32] window, so that the [!INCLUDETLA2#tla_winclient] content can be incorporated into your [!INCLUDETLA#tla_ui] as a child window. The following approach combines the [!INCLUDETLA2#tla_win32] and [!INCLUDETLA2#tla_winclient] in a single application.

  1. Implement your [!INCLUDETLA2#tla_winclient] content (the content root element) as a managed class. Typically, the class inherits from one of the classes that can contain multiple child elements and/or used as a root element, such as xref:System.Windows.Controls.DockPanel or xref:System.Windows.Controls.Page. In subsequent steps, this class is referred to as the [!INCLUDETLA2#tla_winclient] content class, and instances of the class are referred to as [!INCLUDETLA2#tla_winclient] content objects.

  2. Implement a [!INCLUDETLA2#tla_win32] application with [!INCLUDETLA2#tla_cppcli]. If you are starting with an existing unmanaged [!INCLUDETLA2#tla_cpp] application, you can usually enable it to call managed code by changing your project settings to include the /clr compiler flag (the full scope of what might be necessary to support /clr compilation is not described in this topic).

  3. Set the threading model to Single Threaded Apartment (STA). [!INCLUDETLA2#tla_winclient] uses this threading model.

  4. Handle the WM_CREATE notification in your window procedure.

  5. Within the handler (or a function that the handler calls), do the following:

    1. Create a new xref:System.Windows.Interop.HwndSource object with the parent window HWND as its parent parameter.

    2. Create an instance of your [!INCLUDETLA2#tla_winclient] content class.

    3. Assign a reference to the [!INCLUDETLA2#tla_winclient] content object to the xref:System.Windows.Interop.HwndSource object xref:System.Windows.Interop.HwndSource.RootVisual%2A property.

    4. The xref:System.Windows.Interop.HwndSource object xref:System.Windows.Interop.HwndSource.Handle%2A property contains the window handle (HWND). To get an HWND that you can use in the unmanaged part of your application, cast Handle.ToPointer() to an HWND.

  6. Implement a managed class that contains a static field that holds a reference to your [!INCLUDETLA2#tla_winclient] content object. This class allows you to get a reference to the [!INCLUDETLA2#tla_winclient] content object from your [!INCLUDETLA2#tla_win32] code, but more importantly it prevents your xref:System.Windows.Interop.HwndSource from being inadvertently garbage collected.

  7. Receive notifications from the [!INCLUDETLA2#tla_winclient] content object by attaching a handler to one or more of the [!INCLUDETLA2#tla_winclient] content object events.

  8. Communicate with the [!INCLUDETLA2#tla_winclient] content object by using the reference that you stored in the static field to set properties, call methods, etc.

[!NOTE] You can do some or all of the [!INCLUDETLA2#tla_winclient] content class definition for Step One in [!INCLUDETLA2#tla_xaml] using the default partial class of the content class, if you produce a separate assembly and then reference it. Although you typically include an xref:System.Windows.Application object as part of compiling the [!INCLUDETLA2#tla_xaml] into an assembly, you do not end up using that xref:System.Windows.Application as part of the interoperation, you just use one or more of the root classes for [!INCLUDETLA2#tla_xaml] files referred to by the application and reference their partial classes. The remainder of the procedure is essentially similar to that outlined above.

Each of these steps is illustrated through code in the topic Walkthrough: Hosting WPF Content in Win32.

Hosting a Microsoft Win32 Window in WPF

The key to hosting a [!INCLUDETLA2#tla_win32] window within other [!INCLUDETLA2#tla_winclient] content is the xref:System.Windows.Interop.HwndHost class. This class wraps the window in a [!INCLUDETLA2#tla_winclient] element which can be added to a [!INCLUDETLA2#tla_winclient] element tree. xref:System.Windows.Interop.HwndHost also supports [!INCLUDETLA2#tla_api#plural] that allow you to do such tasks as process messages for the hosted window. The basic procedure is:

  1. Create an element tree for a [!INCLUDETLA2#tla_winclient] application (can be through code or markup). Find an appropriate and permissible point in the element tree where the xref:System.Windows.Interop.HwndHost implementation can be added as a child element. In the remainder of these steps, this element is referred to as the reserving element.

  2. Derive from xref:System.Windows.Interop.HwndHost to create an object that holds your [!INCLUDETLA2#tla_win32] content.

  3. In that host class, override the xref:System.Windows.Interop.HwndHost method xref:System.Windows.Interop.HwndHost.BuildWindowCore%2A. Return the HWND of the hosted window. You might want to wrap the actual control(s) as a child window of the returned window; wrapping the controls in a host window provides a simple way for your [!INCLUDETLA2#tla_winclient] content to receive notifications from the controls. This technique helps correct for some [!INCLUDETLA2#tla_win32] issues regarding message handling at the hosted control boundary.

  4. Override the xref:System.Windows.Interop.HwndHost methods xref:System.Windows.Interop.HwndHost.DestroyWindowCore%2A and xref:System.Windows.Interop.HwndHost.WndProc%2A. The intention here is to process cleanup and remove references to the hosted content, particularly if you created references to unmanaged objects.

  5. In your code-behind file, create an instance of the control hosting class and make it a child of the reserving element. Typically you would use an event handler such as xref:System.Windows.FrameworkElement.Loaded, or use the partial class constructor. But you could also add the interoperation content through a runtime behavior.

  6. Process selected window messages, such as control notifications. There are two approaches. Both provide identical access to the message stream, so your choice is largely a matter of programming convenience.

    • Implement message processing for all messages (not just shutdown messages) in your override of the xref:System.Windows.Interop.HwndHost method xref:System.Windows.Interop.HwndHost.WndProc%2A.

    • Have the hosting [!INCLUDETLA2#tla_winclient] element process the messages by handling the xref:System.Windows.Interop.HwndHost.MessageHook event. This event is raised for every message that is sent to the main window procedure of the hosted window.

    • You cannot process messages from windows that are out of process using xref:System.Windows.Interop.HwndHost.WndProc%2A.

  7. Communicate with the hosted window by using platform invoke to call the unmanaged SendMessage function.

Following these steps creates an application that works with mouse input. You can add tabbing support for your hosted window by implementing the xref:System.Windows.Interop.IKeyboardInputSink interface.

Each of these steps is illustrated through code in the topic Walkthrough: Hosting a Win32 Control in WPF.

Hwnds Inside WPF

You can think of xref:System.Windows.Interop.HwndHost as a special control. (Technically, xref:System.Windows.Interop.HwndHost is a xref:System.Windows.FrameworkElement derived class, not a xref:System.Windows.Controls.Control derived class, but it can be considered a control for purposes of interoperation.) xref:System.Windows.Interop.HwndHost abstracts the underlying [!INCLUDETLA2#tla_win32] nature of the hosted content such that the remainder of [!INCLUDETLA2#tla_winclient] considers the hosted content to be another control-like object, which should render and process input. xref:System.Windows.Interop.HwndHost generally behaves like any other [!INCLUDETLA2#tla_winclient] xref:System.Windows.FrameworkElement, although there are some important differences around output (drawing and graphics) and input (mouse and keyboard) based on limitations of what the underlying HWNDs can support.

Notable Differences in Output Behavior

  • xref:System.Windows.FrameworkElement, which is the xref:System.Windows.Interop.HwndHost base class, has quite a few properties that imply changes to the UI. These include properties such as xref:System.Windows.FrameworkElement.FlowDirection%2A?displayProperty=nameWithType, which changes the layout of elements within that element as a parent. However, most of these properties are not mapped to possible [!INCLUDETLA2#tla_win32] equivalents, even if such equivalents might exist. Too many of these properties and their meanings are too rendering-technology specific for mappings to be practical. Therefore, setting properties such as xref:System.Windows.FrameworkElement.FlowDirection%2A on xref:System.Windows.Interop.HwndHost has no effect.

  • xref:System.Windows.Interop.HwndHost cannot be rotated, scaled, skewed, or otherwise affected by a Transform.

  • xref:System.Windows.Interop.HwndHost does not support the xref:System.Windows.UIElement.Opacity%2A property (alpha blending). If content inside the xref:System.Windows.Interop.HwndHost performs xref:System.Drawing operations that include alpha information, that is itself not a violation, but the xref:System.Windows.Interop.HwndHost as a whole only supports Opacity = 1.0 (100%).

  • xref:System.Windows.Interop.HwndHost will appear on top of other [!INCLUDETLA2#tla_winclient] elements in the same top-level window. However, a xref:System.Windows.Controls.ToolTip or xref:System.Windows.Controls.ContextMenu generated menu is a separate top-level window, and so will behave correctly with xref:System.Windows.Interop.HwndHost.

  • xref:System.Windows.Interop.HwndHost does not respect the clipping region of its parent xref:System.Windows.UIElement. This is potentially an issue if you attempt to put an xref:System.Windows.Interop.HwndHost class inside a scrolling region or xref:System.Windows.Controls.Canvas.

Notable Differences in Input Behavior

  • In general, while input devices are scoped within the xref:System.Windows.Interop.HwndHost hosted [!INCLUDETLA2#tla_win32] region, input events go directly to [!INCLUDETLA2#tla_win32].

  • While the mouse is over the xref:System.Windows.Interop.HwndHost, your application does not receive [!INCLUDETLA2#tla_winclient] mouse events, and the value of the [!INCLUDETLA2#tla_winclient] property xref:System.Windows.UIElement.IsMouseOver%2A will be false.

  • While the xref:System.Windows.Interop.HwndHost has keyboard focus, your application will not receive [!INCLUDETLA2#tla_winclient] keyboard events and the value of the [!INCLUDETLA2#tla_winclient] property xref:System.Windows.UIElement.IsKeyboardFocusWithin%2A will be false.

  • When focus is within the xref:System.Windows.Interop.HwndHost and changes to another control inside the xref:System.Windows.Interop.HwndHost, your application will not receive the [!INCLUDETLA2#tla_winclient] events xref:System.Windows.UIElement.GotFocus or xref:System.Windows.UIElement.LostFocus.

  • Related stylus properties and events are analogous, and do not report information while the stylus is over xref:System.Windows.Interop.HwndHost.

Tabbing, Mnemonics, and Accelerators

The xref:System.Windows.Interop.IKeyboardInputSink and xref:System.Windows.Interop.IKeyboardInputSite interfaces allow you to create a seamless keyboard experience for mixed [!INCLUDETLA2#tla_winclient] and [!INCLUDETLA2#tla_win32] applications:

  • Tabbing between [!INCLUDETLA2#tla_win32] and [!INCLUDETLA2#tla_winclient] components

  • Mnemonics and accelerators that work both when focus is within a Win32 component and when it is within a WPF component.

The xref:System.Windows.Interop.HwndHost and xref:System.Windows.Interop.HwndSource classes both provide implementations of xref:System.Windows.Interop.IKeyboardInputSink, but they may not handle all the input messages that you want for more advanced scenarios. Override the appropriate methods to get the keyboard behavior you want.

The interfaces only provide support for what happens on the transition between the [!INCLUDETLA2#tla_winclient] and [!INCLUDETLA2#tla_win32] regions. Within the [!INCLUDETLA2#tla_win32] region, tabbing behavior is entirely controlled by the [!INCLUDETLA2#tla_win32] implemented logic for tabbing, if any.

See Also

xref:System.Windows.Interop.HwndHost
xref:System.Windows.Interop.HwndSource
xref:System.Windows.Interop
Walkthrough: Hosting a Win32 Control in WPF
Walkthrough: Hosting WPF Content in Win32