Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wpf and Hwnd unique name

Tags:

wpf

we have a wpf application that should be 'piloted' by a legacy win32 winform application. (We don't own the code ;) )

The legacy application should pilot our application (minimize, bring to front, shut, etc) via windowsclass name we should provide as a configuration parameter written into an ini file.

The problem is we cannot make it work with wpf since if we insert the classname Spy++ gives us, nothing happens. The point is Spi++ returns something like this

 HwndWrapper[MyWpfProgram.exe;;16978ce2-3b8d-4c46-81ee-e1c6d6de4e6d]

where the guid is randomly generated at every run.

Is there any way to solve this issue?

Thank you.

like image 746
Stefano.net Avatar asked Mar 18 '26 02:03

Stefano.net


1 Answers

There is no way to do what I asked. But we found a walkaround. "Simply" embedding the xaml windows within a windows form.

These are the steps we followed:

1 - Add a Windows Form to the project.

2 - Remove the app.xaml and make the new form the entry point of the application.

3 - Since we need the hwnd of the main.xaml we added this prop to its code behind

    public IntPtr Hwnd
    {
        get { return new WindowInteropHelper(this).Handle; }
    }

4 - then from the constructor of the form we create an instance of the wpf window class

    private Main app;
    public ContainerForm()
    {
        InitializeComponent();

        app = new Main();
        ElementHost.EnableModelessKeyboardInterop(app);
    }

we needed

 ElementHost.EnableModelessKeyboardInterop(app);

since we want all the keyboard input to pass from the the windows form to the xaml window

5 - now we want to bond the xpf window to the winform. In order to do that we need to use Windows Api and we do it at the OnShow event of the form (the reason why will be explicated later).

    [DllImport("user32.dll", SetLastError = true)]
    private static extern long SetFocus(IntPtr hWnd);

    [DllImport("user32.dll", SetLastError = true)]
    private static extern long SetParent(IntPtr hWndChild, IntPtr hWndNewParent);


    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);

    [DllImport("user32.dll", EntryPoint = "SetWindowLongA", SetLastError = true)]
    private static extern long SetWindowLong(IntPtr hwnd, int nIndex, long dwNewLong);
    private const int GWL_STYLE = (-16);
    private const int WS_VISIBLE = 0x10000000;

    private void ContainerForm_Shown(object sender, EventArgs e)
    {
        app.Show();
        SetParent(app.Hwnd, this.Handle);
        SetWindowLong(app.Hwnd, GWL_STYLE, WS_VISIBLE);
        MoveWindow(app.Hwnd, 0, 0, this.Width, this.Height, true);

        SetFocus(app.Hwnd);
    }

with

 SetParent(app.Hwnd, this.Handle);

wo do the magic, then with

  SetWindowLong(app.Hwnd, GWL_STYLE, WS_VISIBLE);

we remove al the chrome from the wpf window (there is a border even if the window is defined borderless, don't ask me why)

then we make the wpf window fill all the client area of the winform

  MoveWindow(app.Hwnd, 0, 0, this.Width, this.Height, true);

and then we focus the wpf window

 SetFocus(app.Hwnd);

that's the reason why we do everything in the show event. Since if we do it at form's constructor, then the wpf window will loose its focus since the in winform the main window got the focus from the operating system.

We didn't understand why we needed to add the other api calls at this point, but if we left them at constructor's the trick didn't work.

Anyway, problem solved ;)

like image 73
Stefano.net Avatar answered Mar 20 '26 19:03

Stefano.net



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!