Partager via


You can use HWndHost to host a Win32 HWnd window inside a WPF element

 

Suppose you have some old code lying around that creates a Win32 window with an HWnd (perhaps ATL, MFC, or just C/ C++). For example, if you type some erroneous code into a VB application in Visual Studio:

 

        Dim x=4: catch ex As Exception

 

 

The “Exception” will be squiggled and the end of the squiggle will have a red underline, indicating the presence of a Smart Tag.

 

You can move your mouse over the red underline or hit Ctrl-Period to invoke the smart tag suggestions for fixing the error. In this case, 2 suggested fixes are suggested in the smart tag menu: to insert Try/End Try in various places.

 

These suggested fixes show a preview right in the smart tag menu of what the effect is of each possible correction. These windows were written originally with Win32 Hwnds, using ATL.

 

You might want to take these old Windows and host them in a more modern application, using Windows Presentation Foundation

 

 

The sample below creates 4 identical Win32 HWnd windows hosted inside an HwndHost control. This control is then put into 3 WPF Popups and 1 WPF Window. The Win32 windows are simple and the WM_PAINT message just paints green & blue rectangles.

 

The 3 popups are identical, except that:

#1 has AllowTransparency = false. The Win32 hWnd window shows just fine

#2 has AllowTransparency = true. The Win32 hWnd window doesn’t show at all, but it can be discovered by using Spy++

#3 has AllowTransparency = true, but with the StackPanel inside with a Transparent Background. The Win32 hWnd doesn’t show at all, and you can even click through the popup

 

 

A Popup with AllowsTransparency = True requires Full Trust security. See https://blogs.msdn.com/dwayneneed/archive/2008/09/08/transparent-windows-in-wpf.aspx

 

 

 

 

Start Visual Studio (I was using VS 2008)

 

Choose File->New Project->Visual Basic->WPF Application.

(This also works with Temporary projects)

 

Open Window1.xaml.vb. Paste in the code below, then hit F5 to run. Try clicking on the various areas.

 

See also:

Spy on your programs

 

CreateWindowEx

 

FillRect

 

Pinvoke.Net

<Sample Code>

Option Strict On

Imports System.Runtime.InteropServices

Imports System.Windows.Interop

Class Window1

    Sub Load() Handles MyBase.Loaded

        Me.Width = 600

        Me.Height = 600

        Dim popupNoTransparency = CreatePopup(False, "NoTransP", "AliceBlue", 0)

        Dim popupAllowTransparency = CreatePopup(True, "AllowTransp", "AliceBlue", 300)

        Dim popupTransparencyWithTransBack = CreatePopup(True, "popupTransparencyWithTransBack", "Transparent", 600)

        Me.Content = CreateUIElem("Main Window", "Bisque")

    End Sub

    Function CreatePopup(ByVal AllowTransparency As Boolean, ByVal Name As String, ByVal BackGround As String, ByVal Horiz As Integer) As Primitives.Popup

        Dim popup = New Primitives.Popup

        popup.AllowsTransparency = AllowTransparency

        popup.Child = CreateUIElem(Name, BackGround)

        popup.Placement = Primitives.PlacementMode.AbsolutePoint

        popup.HorizontalOffset = Horiz

        popup.IsOpen = True

        Return popup

    End Function

    Function CreateUIElem(ByVal UIElemName As String, ByVal BackGround As String) As UIElement

        Dim xaml = _

        <StackPanel

            xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"

            xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"

  Name="HwndTest" Width="350" Height="450" Background=<%= BackGround %> Orientation="Vertical" Opacity=".5">

            <Button><%= UIElemName %></Button>

            <UserControl Name="MyBorder"></UserControl>

            <Button>Bottom Button</Button>

            <TextBlock Name="Another">some text</TextBlock>

        </StackPanel>

        Dim UIElem = CType(System.Windows.Markup.XamlReader.Load(xaml.CreateReader), StackPanel)

        Dim Border = CType(UIElem.FindName("MyBorder"), UserControl)

        Dim oMyHwndHost = New MyHwndHost

        Border.Content = oMyHwndHost

        Return UIElem

    End Function

End Class

Class MyHwndHost

    Inherits HwndHost

    Protected Overrides Function BuildWindowCore(ByVal hwndParent As System.Runtime.InteropServices.HandleRef) As System.Runtime.InteropServices.HandleRef

        Dim hwndMain As IntPtr = CreateWindowEx(0, "static", "", WindowStyles.WS_CHILD Or WindowStyles.WS_HSCROLL Or WindowStyles.WS_VSCROLL, _

                                     0, 0, 200, 300, hwndParent.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero)

        Return New HandleRef(Me, hwndMain)

    End Function

    Protected Overrides Sub DestroyWindowCore(ByVal hwnd As System.Runtime.InteropServices.HandleRef)

        DestroyWindow(hwnd.Handle)

    End Sub

    Protected Overrides Function WndProc(ByVal hwnd As System.IntPtr, ByVal msg As Integer, ByVal wParam As System.IntPtr, ByVal lParam As System.IntPtr, ByRef handled As Boolean) As System.IntPtr

        Select Case msg

       Case WM_.WM_PAINT

                Dim ps As New PAINTSTRUCT

                BeginPaint(hwnd, ps)

                Dim hDC = ps.hdc

                Dim r = New RECT

                GetClientRect(hwnd, r)

                r.Right = CInt(r.Right / 2) ' create a rect of left half

                Dim hbrGreen = CreateSolidBrush(CType(&HFF00, IntPtr))

                Dim hbrBlue = CreateSolidBrush(CType(&HFF0000, IntPtr))

                FillRect(hDC, r, hbrGreen)

                r.Left += r.Right - r.Left

                r.Right *= 2 ' create a rect of right half

                FillRect(hDC, r, hbrBlue)

                EndPaint(hwnd, ps)

                DeleteObject(hbrGreen)

                DeleteObject(hbrBlue)

                handled = True

      Return IntPtr.Zero

        End Select

        Return MyBase.WndProc(hwnd, msg, wParam, lParam, handled)

    End Function

#Region "WIN32 Defs"

    Public Enum COLOR

        COLOR_SCROLLBAR = 0

        COLOR_BACKGROUND = 1

        COLOR_DESKTOP = 1

        COLOR_ACTIVECAPTION = 2

        COLOR_INACTIVECAPTION = 3

        COLOR_MENU = 4

        COLOR_WINDOW = 5

        COLOR_WINDOWFRAME = 6

        COLOR_MENUTEXT = 7

        COLOR_WINDOWTEXT = 8

        COLOR_CAPTIONTEXT = 9

        COLOR_ACTIVEBORDER = 10

        COLOR_INACTIVEBORDER = 11

        COLOR_APPWORKSPACE = 12

        COLOR_HIGHLIGHT = 13

        COLOR_HIGHLIGHTTEXT = 14

        COLOR_BTNFACE = 15

        COLOR_3DFACE = 15

        COLOR_BTNSHADOW = 16

        COLOR_3DSHADOW = 16

        COLOR_GRAYTEXT = 17

        COLOR_BTNTEXT = 18

        COLOR_INACTIVECAPTIONTEXT = 19

        COLOR_BTNHIGHLIGHT = 20

        COLOR_3DHIGHLIGHT = 20

        COLOR_3DHILIGHT = 20

        COLOR_BTNHILIGHT = 20

        COLOR_3DDKSHADOW = 21

    COLOR_3DLIGHT = 22

        COLOR_INFOTEXT = 23

        COLOR_INFOBK = 24

    End Enum

    <StructLayout(LayoutKind.Sequential)> _

        Public Structure RECT

        Public Left As Integer

        Public Top As Integer

        Public Right As Integer

        Public Bottom As Integer

        Public Function ToRect() As System.Windows.Rect

            Return New System.Windows.Rect(Left, Top, Right - Left, Bottom - Top)

        End Function

    End Structure

    <DllImport("user32.dll", CharSet:=CharSet.Auto)> _

    Private Shared Function GetClientRect(ByVal hWnd As System.IntPtr, _

       ByRef lpRECT As RECT) As Integer

    End Function

    Enum WM_

        WM_PAINT = &HF

    End Enum

    <Flags()> _

    Enum WindowStyles

        WS_OVERLAPPED = &H0

        WS_POPUP = &H80000000

        WS_CHILD = &H40000000

        WS_MINIMIZE = &H20000000

        WS_VISIBLE = &H10000000

        WS_DISABLED = &H8000000

        WS_CLIPSIBLINGS = &H4000000

        WS_CLIPCHILDREN = &H2000000

       WS_MAXIMIZE = &H1000000

        WS_BORDER = &H800000

        WS_DLGFRAME = &H400000

        WS_VSCROLL = &H200000

        WS_HSCROLL = &H100000

        WS_SYSMENU = &H80000

        WS_THICKFRAME = &H40000

        WS_GROUP = &H20000

        WS_TABSTOP = &H10000

        WS_MINIMIZEBOX = &H20000

        WS_MAXIMIZEBOX = &H10000

        WS_CAPTION = WS_BORDER Or WS_DLGFRAME

        WS_TILED = WS_OVERLAPPED

        WS_ICONIC = WS_MINIMIZE

        WS_SIZEBOX = WS_THICKFRAME

        WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW

        WS_OVERLAPPEDWINDOW = WS_OVERLAPPED Or WS_CAPTION Or WS_SYSMENU Or WS_THICKFRAME Or WS_MINIMIZEBOX Or WS_MAXIMIZEBOX

        WS_POPUPWINDOW = WS_POPUP Or WS_BORDER Or WS_SYSMENU

        WS_CHILDWINDOW = WS_CHILD

    End Enum

    <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _

    Private Shared Function ShowWindow(ByVal hwnd As IntPtr, ByVal nCmdShow As Int32) As Boolean

    End Function

    <DllImport("user32.dll")> _

    Public Shared Function UpdateWindow( _

     ByVal hWnd As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean

    End Function

    <DllImport("user32.dll", CharSet:=CharSet.Auto)> _

    Private Shared Function CreateWindowEx( _

         ByVal dwExStyle As UInteger, _

         ByVal lpClassName As String, _

         ByVal lpWindowName As String, _

         ByVal dwStyle As WindowStyles, _

         ByVal x As Integer, _

         ByVal y As Integer, _

         ByVal nWidth As Integer, _

         ByVal nHeight As Integer, _

         ByVal hWndParent As IntPtr, _

         ByVal hMenut As IntPtr, _

         ByVal hInstancet As IntPtr, _

         ByVal lpParamt As IntPtr) As IntPtr

    End Function

#If 0 Then

hwndMain = CreateWindowEx(

    0, // no extended styles

    "MainWClass", // class name

    "Main Window", // window name

    WS_OVERLAPPEDWINDOW | // overlapped window

             WS_HSCROLL | // horizontal scroll bar

             WS_VSCROLL, // vertical scroll bar

    CW_USEDEFAULT, // default horizontal position

    CW_USEDEFAULT, // default vertical position

    CW_USEDEFAULT, // default width

    CW_USEDEFAULT, // default height

    (HWND) NULL, // no parent or owner window

    (HMENU) NULL, // class menu used

    hinstance, // instance handle

    NULL); // no window creation data

#End If

    <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _

    Private Shared Function DestroyWindow(ByVal hwnd As IntPtr) As Boolean

    End Function

    Const CW_USEDEFAULT As Int32 = &H80000000

    Enum Show_Window

        SW_HIDE = 0

        SW_SHOWNORMAL = 1

        SW_NORMAL = 1

        SW_SHOWMINIMIZED = 2

        SW_SHOWMAXIMIZED = 3

        SW_MAXIMIZE = 3

        SW_SHOWNOACTIVATE = 4

        SW_SHOW = 5

        SW_MINIMIZE = 6

        SW_SHOWMINNOACTIVE = 7

        SW_SHOWNA = 8

        SW_RESTORE = 9

        SW_SHOWDEFAULT = 10

        SW_FORCEMINIMIZE = 11

        SW_MAX = 11

    End Enum

    <StructLayout(LayoutKind.Sequential, Pack:=4)> _

      Public Structure PAINTSTRUCT

        Public hdc As IntPtr

        Public fErase As Integer

        Public rcPaint As RECT

        Public fRestore As Integer

        Public fIncUpdate As Integer

        <MarshalAs(UnmanagedType.ByValArray, SizeConst:=32)> _

        Public rgbReserved As Byte()

  End Structure

    <DllImport("user32.dll")> _

    Public Shared Function BeginPaint( _

     ByVal hWnd As IntPtr, ByRef lpPaint As PAINTSTRUCT) As IntPtr

    End Function

    <DllImport("user32.dll")> _

    Public Shared Function EndPaint( _

     ByVal hWnd As IntPtr, ByRef lpPaint As PAINTSTRUCT) As IntPtr

    End Function

    <DllImport("user32.dll")> _

    Public Shared Function FillRect(ByVal hDC As IntPtr, ByRef lpRect As RECT, ByVal hBR As IntPtr) As IntPtr

    End Function

    <DllImport("gdi32.dll")> _

    Public Shared Function CreateSolidBrush(ByVal crColor As IntPtr) As IntPtr

    End Function

    <DllImport("gdi32.dll")> _

    Public Shared Function DeleteObject(ByVal hObject As IntPtr) As IntPtr

    End Function

#End Region

End Class

</Sample Code>

Comments

  • Anonymous
    June 18, 2009
    PingBack from http://barstoolsite.info/story.php?id=7173

  • Anonymous
    April 03, 2014
    This doesn't work for me. I get three of the external MainWindow in my Visual Studio IDE screen, at the top left, horizontally next to each other, and one WPF MainWindow with the created MainWindow inside it, scrollbars and buttons not working.