--- title: "Hosting Win32 Content" ms.date: "03/30/2017" helpviewer_keywords: - "interoperability [WPF], tutorials" - "Win32 code [WPF], WPF interoperation" - "interoperability [WPF], Win32" ms.assetid: 3cc8644a-34f3-4082-9ddc-77623e4df2d8 --- # Hosting Win32 Content in WPF ## Prerequisites See [WPF and Win32 Interoperation](wpf-and-win32-interoperation.md). ## A Walkthrough of Win32 Inside Windows Presentation Framework (HwndHost) To reuse Win32 content inside [!INCLUDE[TLA2#tla_winclient](../../../includes/tla2sharptla-winclient-md.md)] applications, use , which is a control that makes HWNDs look like [!INCLUDE[TLA2#tla_winclient](../../../includes/tla2sharptla-winclient-md.md)] content. Like , is straightforward to use: derive from and implement `BuildWindowCore` and `DestroyWindowCore` methods, then instantiate your derived class and place it inside your [!INCLUDE[TLA2#tla_winclient](../../../includes/tla2sharptla-winclient-md.md)] application. If your Win32 logic is already packaged as a control, then your `BuildWindowCore` implementation is little more than a call to `CreateWindow`. For example, to create a Win32 LISTBOX control in C++: ```cpp virtual HandleRef BuildWindowCore(HandleRef hwndParent) override { HWND handle = CreateWindowEx(0, L"LISTBOX", L"this is a Win32 listbox", WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_VSCROLL | WS_BORDER, 0, 0, // x, y 30, 70, // height, width (HWND) hwndParent.Handle.ToPointer(), // parent hwnd 0, // hmenu 0, // hinstance 0); // lparam return HandleRef(this, IntPtr(handle)); } virtual void DestroyWindowCore(HandleRef hwnd) override { // HwndHost will dispose the hwnd for us } ``` But suppose the Win32 code is not quite so self-contained? If so, you can create a Win32 dialog box and embed its contents into a larger [!INCLUDE[TLA2#tla_winclient](../../../includes/tla2sharptla-winclient-md.md)] application. The sample shows this in Visual Studio and C++, although it is also possible to do this in a different language or at the command line. Start with a simple dialog, which is compiled into a C++ DLL project. Next, introduce the dialog into the larger [!INCLUDE[TLA2#tla_winclient](../../../includes/tla2sharptla-winclient-md.md)] application: - Compile the DLL as managed (`/clr`) - Turn the dialog into a control - Define the derived class of with `BuildWindowCore` and `DestroyWindowCore` methods - Override `TranslateAccelerator` method to handle dialog keys - Override `TabInto` method to support tabbing - Override `OnMnemonic` method to support mnemonics - Instantiate the subclass and put it under the right [!INCLUDE[TLA2#tla_winclient](../../../includes/tla2sharptla-winclient-md.md)] element ### Turn the Dialog into a Control You can turn a dialog box into a child HWND using the WS_CHILD and DS_CONTROL styles. Go into the resource file (.rc) where the dialog is defined, and find the beginning of the definition of the dialog: ```text IDD_DIALOG1 DIALOGEX 0, 0, 303, 121 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU ``` Change the second line to: ```text STYLE DS_SETFONT | WS_CHILD | WS_BORDER | DS_CONTROL ``` This action does not fully package it into a self-contained control; you still need to call `IsDialogMessage()` so Win32 can process certain messages, but the control change does provide a straightforward way of putting those controls inside another HWND. ## Subclass HwndHost Import the following namespaces: ```cpp namespace ManagedCpp { using namespace System; using namespace System::Windows; using namespace System::Windows::Interop; using namespace System::Windows::Input; using namespace System::Windows::Media; using namespace System::Runtime::InteropServices; ``` Then create a derived class of and override the `BuildWindowCore` and `DestroyWindowCore` methods: ```cpp public ref class MyHwndHost : public HwndHost, IKeyboardInputSink { private: HWND dialog; protected: virtual HandleRef BuildWindowCore(HandleRef hwndParent) override { InitializeGlobals(); dialog = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), (HWND) hwndParent.Handle.ToPointer(), (DLGPROC) About); return HandleRef(this, IntPtr(dialog)); } virtual void DestroyWindowCore(HandleRef hwnd) override { // hwnd will be disposed for us } ``` Here you use the `CreateDialog` to create the dialog box that is really a control. Since this is one of the first methods called inside the DLL, you should also do some standard Win32 initialization by calling a function you will define later, called `InitializeGlobals()`: ```cpp bool initialized = false; void InitializeGlobals() { if (initialized) return; initialized = true; // TODO: Place code here. MSG msg; HACCEL hAccelTable; // Initialize global strings LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_TYPICALWIN32DIALOG, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); ``` ### Override TranslateAccelerator Method to Handle Dialog Keys If you ran this sample now, you would get a dialog control that displays, but it would ignore all of the keyboard processing that makes a dialog box a functional dialog box. You should now override the `TranslateAccelerator` implementation (which comes from `IKeyboardInputSink`, an interface that implements). This method gets called when the application receives WM_KEYDOWN and WM_SYSKEYDOWN. ```cpp #undef TranslateAccelerator virtual bool TranslateAccelerator(System::Windows::Interop::MSG% msg, ModifierKeys modifiers) override { ::MSG m = ConvertMessage(msg); // Win32's IsDialogMessage() will handle most of our tabbing, but doesn't know // what to do when it reaches the last tab stop if (m.message == WM_KEYDOWN && m.wParam == VK_TAB) { HWND firstTabStop = GetDlgItem(dialog, IDC_EDIT1); HWND lastTabStop = GetDlgItem(dialog, IDCANCEL); TraversalRequest^ request = nullptr; if (GetKeyState(VK_SHIFT) && GetFocus() == firstTabStop) { // this code should work, but there’s a bug with interop shift-tab in current builds request = gcnew TraversalRequest(FocusNavigationDirection::Last); } else if (!GetKeyState(VK_SHIFT) && GetFocus() == lastTabStop) { request = gcnew TraversalRequest(FocusNavigationDirection::Next); } if (request != nullptr) return ((IKeyboardInputSink^) this)->KeyboardInputSite->OnNoMoreTabStops(request); } // Only call IsDialogMessage for keys it will do something with. if (msg.message == WM_SYSKEYDOWN || msg.message == WM_KEYDOWN) { switch (m.wParam) { case VK_TAB: case VK_LEFT: case VK_UP: case VK_RIGHT: case VK_DOWN: case VK_EXECUTE: case VK_RETURN: case VK_ESCAPE: case VK_CANCEL: IsDialogMessage(dialog, &m); // IsDialogMessage should be called ProcessDialogMessage -- // it processes messages without ever really telling you // if it handled a specific message or not return true; } } return false; // not a key we handled } ``` This is a lot of code in one piece, so it could use some more detailed explanations. First, the code using C++ and C++ macros; you need to be aware that there is already a macro named `TranslateAccelerator`, which is defined in winuser.h: ```cpp #define TranslateAccelerator TranslateAcceleratorW ``` So make sure to define a `TranslateAccelerator` method and not a `TranslateAcceleratorW` method. Similarly, there is both the unmanaged winuser.h MSG and the managed `Microsoft::Win32::MSG` struct. You can disambiguate between the two using the C++ `::` operator. ```cpp virtual bool TranslateAccelerator(System::Windows::Interop::MSG% msg, ModifierKeys modifiers) override { ::MSG m = ConvertMessage(msg); } ``` Both MSGs have the same data, but sometimes it is easier to work with the unmanaged definition, so in this sample you can define the obvious conversion routine: ```cpp ::MSG ConvertMessage(System::Windows::Interop::MSG% msg) { ::MSG m; m.hwnd = (HWND) msg.hwnd.ToPointer(); m.lParam = (LPARAM) msg.lParam.ToPointer(); m.message = msg.message; m.wParam = (WPARAM) msg.wParam.ToPointer(); m.time = msg.time; POINT pt; pt.x = msg.pt_x; pt.y = msg.pt_y; m.pt = pt; return m; } ``` Back to `TranslateAccelerator`. The basic principle is to call the Win32 function `IsDialogMessage` to do as much work as possible, but `IsDialogMessage` does not have access to anything outside the dialog. As a user tab around the dialog, when tabbing runs past the last control in our dialog, you need to set focus to the [!INCLUDE[TLA2#tla_winclient](../../../includes/tla2sharptla-winclient-md.md)] portion by calling `IKeyboardInputSite::OnNoMoreStops`. ```cpp // Win32's IsDialogMessage() will handle most of the tabbing, but doesn't know // what to do when it reaches the last tab stop if (m.message == WM_KEYDOWN && m.wParam == VK_TAB) { HWND firstTabStop = GetDlgItem(dialog, IDC_EDIT1); HWND lastTabStop = GetDlgItem(dialog, IDCANCEL); TraversalRequest^ request = nullptr; if (GetKeyState(VK_SHIFT) && GetFocus() == firstTabStop) { request = gcnew TraversalRequest(FocusNavigationDirection::Last); } else if (!GetKeyState(VK_SHIFT) && GetFocus() == lastTabStop) { { request = gcnew TraversalRequest(FocusNavigationDirection::Next); } if (request != nullptr) return ((IKeyboardInputSink^) this)->KeyboardInputSite->OnNoMoreTabStops(request); } ``` Finally, call `IsDialogMessage`. But one of the responsibilities of a `TranslateAccelerator` method is telling [!INCLUDE[TLA2#tla_winclient](../../../includes/tla2sharptla-winclient-md.md)] whether you handled the keystroke or not. If you did not handle it, the input event can tunnel and bubble through the rest of the application. Here, you will expose a quirk of keyboard messange handling and the nature of the input architecture in Win32. Unfortunately, `IsDialogMessage` does not return in any way whether it handles a particular keystroke. Even worse, it will call `DispatchMessage()` on keystrokes it should not handle! So you will have to reverse-engineer `IsDialogMessage`, and only call it for the keys you know it will handle: ```cpp // Only call IsDialogMessage for keys it will do something with. if (msg.message == WM_SYSKEYDOWN || msg.message == WM_KEYDOWN) { switch (m.wParam) { case VK_TAB: case VK_LEFT: case VK_UP: case VK_RIGHT: case VK_DOWN: case VK_EXECUTE: case VK_RETURN: case VK_ESCAPE: case VK_CANCEL: IsDialogMessage(dialog, &m); // IsDialogMessage should be called ProcessDialogMessage -- // it processes messages without ever really telling you // if it handled a specific message or not return true; } ``` ### Override TabInto Method to Support Tabbing Now that you have implemented `TranslateAccelerator`, a user can tab around inside the dialog box and tab out of it into the greater [!INCLUDE[TLA2#tla_winclient](../../../includes/tla2sharptla-winclient-md.md)] application. But a user cannot tab back into the dialog box. To solve that, you override `TabInto`: ```cpp public: virtual bool TabInto(TraversalRequest^ request) override { if (request->FocusNavigationDirection == FocusNavigationDirection::Last) { HWND lastTabStop = GetDlgItem(dialog, IDCANCEL); SetFocus(lastTabStop); } else { HWND firstTabStop = GetDlgItem(dialog, IDC_EDIT1); SetFocus(firstTabStop); } return true; } ``` The `TraversalRequest` parameter tells you whether the tab action is a tab or shift tab. ### Override OnMnemonic Method to Support Mnemonics Keyboard handling is almost complete, but there is one thing missing – mnemonics do not work. If a user presses alt-F, focus doe not jump to the "First name:" edit box. So, you override the `OnMnemonic` method: ```cpp virtual bool OnMnemonic(System::Windows::Interop::MSG% msg, ModifierKeys modifiers) override { ::MSG m = ConvertMessage(msg); // If it's one of our mnemonics, set focus to the appropriate hwnd if (msg.message == WM_SYSCHAR && GetKeyState(VK_MENU /*alt*/)) { int dialogitem = 9999; switch (m.wParam) { case 's': dialogitem = IDOK; break; case 'c': dialogitem = IDCANCEL; break; case 'f': dialogitem = IDC_EDIT1; break; case 'l': dialogitem = IDC_EDIT2; break; case 'p': dialogitem = IDC_EDIT3; break; case 'a': dialogitem = IDC_EDIT4; break; case 'i': dialogitem = IDC_EDIT5; break; case 't': dialogitem = IDC_EDIT6; break; case 'z': dialogitem = IDC_EDIT7; break; } if (dialogitem != 9999) { HWND hwnd = GetDlgItem(dialog, dialogitem); SetFocus(hwnd); return true; } } return false; // key unhandled }; ``` Why not call `IsDialogMessage` here? You have the same issue as before--you need to be able to inform [!INCLUDE[TLA2#tla_winclient](../../../includes/tla2sharptla-winclient-md.md)] code whether your code handled the keystroke or not, and `IsDialogMessage` cannot do that. There is also a second issue, because `IsDialogMessage` refuses to process the mnemonic if the focused HWND is not inside the dialog box. ### Instantiate the HwndHost Derived Class Finally, now that all the key and tab support is in place, you can put your into the larger [!INCLUDE[TLA2#tla_winclient](../../../includes/tla2sharptla-winclient-md.md)] application. If the main application is written in [!INCLUDE[TLA2#tla_xaml](../../../includes/tla2sharptla-xaml-md.md)], the easiest way to put it in the right place is to leave an empty element where you want to put the . Here you create a named `insertHwndHostHere`: ```xaml