6

I'd like to host a window of an external process inside my WPF application. I'm deriving HwndHost like this:

    class HwndHostEx : HwndHost
    {
        [DllImport("user32.dll")]
        static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

        private IntPtr ChildHandle = IntPtr.Zero;

        public HwndHostEx(IntPtr handle)
        {
            this.ChildHandle = handle;
        }

        protected override System.Runtime.InteropServices.HandleRef BuildWindowCore(System.Runtime.InteropServices.HandleRef hwndParent)
        {
            HandleRef href = new HandleRef();

            if (ChildHandle != IntPtr.Zero)
            {
                SetParent(this.ChildHandle, hwndParent.Handle);
                href = new HandleRef(this, this.ChildHandle);
            }

            return href;
        }

        protected override void DestroyWindowCore(System.Runtime.InteropServices.HandleRef hwnd)
        {

        }
    }

and using it like this:

 HwndHostEx host = new HwndHostEx(handle);
 this.PART_Host.Child = host;

where handle is a handle for an external window I'd like to host and PART_Host is a border inside my WPF window:

<StackPanel UseLayoutRounding="True"
        SnapsToDevicePixels="True"
        Background="Transparent">
        <Border Name="PART_Host" />
...

This gives me an exception:

Hosted HWND must be a child window.

Sorry for my lack of knowledge but what is the proper way of hosting an external window inside WPF application?

Mike G
  • 185
  • 5
  • 17
  • 1
    Hi Mike, What is the handle object in HwndHostEx host = new HwndHostEx(handle); ? I am solving the same problem, and I am still could not find a solution. – VivekDev Apr 13 '16 at 15:39
  • 1
    @VivekDev: handle object in HwndHostEx is an IntPtr handle of an external window. You can get this [here](http://stackoverflow.com/questions/820909/get-applications-window-handles) or [here](http://stackoverflow.com/questions/10675305/how-to-get-the-hwnd-of-window-instance) or [here](http://stackoverflow.com/questions/1402848/getting-the-handle-of-window-in-c-sharp). Hope this helps. – Mike G Apr 15 '16 at 06:16

3 Answers3

6

Right before calling 'SetParent' do this:

public const int GWL_STYLE = (-16);
public const int WS_CHILD = 0x40000000;

SetWindowLong(this.ChildHandle, GWL_STYLE, WS_CHILD);
Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
Jose
  • 324
  • 4
  • 6
  • 1
    Note that this completely overrides the current window style. You might like to get the previous value, then set/mask values instead. – Drew Noakes Apr 28 '16 at 10:53
1

Looking at the docs:

  1. You need a Win32 control compiled for CLI
  2. You must create the control inside your WPF app.

It seems not possibile to "attach" an already running Window from another process inside a WPF app.

How do you get the handle you pass to your HwndHostEx constructor?

corradolab
  • 718
  • 3
  • 16
  • Thanks for Your interest. Handle for this external window is returned by one of the methods in the DLL which produces it. What do You mean: "compiled for CLI"? – Mike G May 12 '15 at 11:02
  • 1
    It means your "native code" must have metadata which allows to behave like (and interop with) "managed code". In practice if you have legacy Win32 C/C++ code you should upgrade it to a recent C++ version and compile it with the /clr complier switch. See https://msdn.microsoft.com/en-us/library/ms173265.aspx – corradolab May 12 '15 at 11:21
  • I think that it is already done. The DLL is a C++ code which is wrapped to interop with C#. It shows a window and I'd just like to host it in my app. – Mike G May 12 '15 at 11:57
0

To add clarification, here's the final code for hosting a process in a WPF app, taken from Jose's answer:

    class HwndHostEx : HwndHost
    {
        [DllImport("user32.dll")]
        static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

        [DllImport("user32.dll")]
        static extern int SetWindowLong(IntPtr hWnd, int nIndex, UInt32 dwNewLong);

        private IntPtr ChildHandle = IntPtr.Zero;

        public HwndHostEx(IntPtr handle)
        {
            this.ChildHandle = handle;
        }

        protected override System.Runtime.InteropServices.HandleRef BuildWindowCore(System.Runtime.InteropServices.HandleRef hwndParent)
        {
            HandleRef href = new HandleRef();

            if (ChildHandle != IntPtr.Zero)
            {
                const int GWL_STYLE = (-16);
                const int WS_CHILD = 0x40000000;

                SetWindowLong(this.ChildHandle, GWL_STYLE, WS_CHILD);


                SetParent(this.ChildHandle, hwndParent.Handle);
                href = new HandleRef(this, this.ChildHandle);
            }

            return href;
        }

        protected override void DestroyWindowCore(System.Runtime.InteropServices.HandleRef hwnd)
        {

        }
    }

// to create an instance:
var processName = "Whatever.exe";
var process = System.Diagnostics.Process.GetProcesses()
 .FirstOrDefault(item => item.ProcessName.ToLowerInvariant() == processName && item.MainWindowHandle != IntPtr.Zero);
var handle = process?.MainWindowHandle;

if(handle != null)
{
    var host = new HwndHostEx(handle.Value);

    YourControl.Grid.Children.Add(host);
}


Victor Chelaru
  • 4,491
  • 3
  • 35
  • 49