Can I get the behavior of setting my WinForms form's owner using an hwnd / NativeWindow?
Asked Answered
Q

2

6

My application is a vb6 executable, but some newer forms in the system are written in C#. I would like to be able to set the C# form's Owner property using a handle to the main application window, so that the dialogs remain on top when tabbing back and forth between my app and other apps.

I can get the hwnd of the main application window. I'm not sure what I can do from there?


UPDATE Oct 20 '08 at 17:06:

Scott,

Thanks for the response. I had overlooked that the Show/ShowDialog method parameter was not of type Form - I was looking only at the Owner property.

I slightly modified the code I'm using from the above - we have a component that generically loads our Forms and calls ShowDialog. My code looks like this:

Form launchTarget = FormFactory.GetForm(xxx);  // psuedo-code for generic form loader
launchTarget.StartPosition = FormStartPosition.CenterParent;
IWin32Window parentWindow = GetWindowFromHwnd(hwnd);

launchTarget.ShowDialog(parentWindow);

GetWindowFromHwnd is a method-wrapped version of your code:

private IWin32Window GetWindowFromHost(int hwnd)
{
    IWin32Window window = null;
    IntPtr handle = new IntPtr(hwnd);

    try
    {
        NativeWindow nativeWindow = new NativeWindow();
        nativeWindow.AssignHandle(handle);
        window = nativeWindow;
    }
    finally
    {
        handle = IntPtr.Zero;
    }

    return window;
}

Unfortunately this isn't doing what I'd hoped. The form does display modally, but it's not showing up in the correct position nor is it still on top when I tab away and back to the parent window. Our modals do not show a task in the taskbar, so the window seemingly "disappears" (although it is still present in the alt-tab window list). That to me indicates I might not have the right hwnd. If you have any other suggestions though, please reply back. Thanks again.


UPDATE Nov 10 '08 at 16:25

One follow up remark - If you factor it out into a method call in a try/finally, as in Scott's 2nd post, the call in the finally block should be:

parentWindow.ReleaseHandle();
Questionnaire answered 17/10, 2008 at 20:7 Comment(2)
Scott - sorry for the lengthy delay, got pulled onto other things. Your original submitted answer works great. I just needed to pass the NativeWindow in through ShowDialog instead of setting Form.Owner. My other problem was the calling code not passing the correct hWnd in the first place. ThanksQuestionnaire
FYI, both of the above problems are solvable -- for the centeredness make sure to set: "form.StartPosition = FormStartPosition.CenterParent" -- for the toppyness make sure to set: form.TopMost = true; -- btw, thanks for the info on ReleaseHandle()! :-)Watch
M
9

So you are calling a C# Windows Form class from VB6, which means you are probably using either Show() or ShowDialog(), correct? Both of those methods also take an IWin32Window parameter, which simply defines an object that returns an IntPtr property named Handle.

So...you need to add an overloaded constructor (or ShowDialog method) for your Windows Forms classes which take a long as a parameter so you can pass the VB6 hwnd to the form. Once inside the C# code, you need to create an IntPtr from the hwnd and assign it to a NativeWindow object and then pass that as the owner.

Something like this should work, although it's untested:

public DialogResult ShowDialog(long hwnd)
{
   IntPtr handle = new IntPtr(hwnd);
   try
   {
      NativeWindow nativeWindow = new NativeWindow();

      nativeWindow.AssignHandle(handle);
      return this.ShowDialog(nativeWindow);
   }
   finally
   {
      handle = IntPtr.Zero;
   }
}
Muddlehead answered 17/10, 2008 at 20:36 Comment(0)
M
2

This is too long to post as a comment...

I think the problem you are running in to is the way you wrapped the code I presented in the ShowDialog overload. If you follow what your GetWindowFromHost code is doing it goes through the following steps:

  1. Creates a new IntPtr from the hwnd given.
  2. Creates a new NativeWindow object and assigns it's handle to be the IntPtr.
  3. Sets the IntPtr (in the finally block) to be IntPtr.Zero.

I think it's this finally block that is causing you problems. In my code, the finally block would run after the call to this.ShowDialog(nativeWindow) finished. At that point the handle (IntPtr) was no longer being used. In your code, you are returning an IWin32Window that should still be holding a reference to that IntPtr, which at the time you call launchTarget.ShowDialog(parentWindow) is IntPtr.Zero.

Try changing your code to look like this:

private NativeWindow GetWindowFromHost(int hwnd)
{
   IntPtr handle = new IntPtr(hwnd);
   NativeWindow nativeWindow = new NativeWindow();
   nativeWindow.AssignHandle(handle);
   return window;
}

And then change your calling code to look like this:

Form launchTarget = FormFactory.GetForm(xxx);  // psuedo-code for generic form 
loaderlaunchTarget.StartPosition = FormStartPosition.CenterParent;
NativeWindow parentWindow = GetWindowFromHwnd(hwnd);

try
{
   launchTarget.ShowDialog(parentWindow);
}
finally
{
   parentWindow.DestroyHandle();
}

These changes should work, but again this is untested.

Muddlehead answered 20/10, 2008 at 17:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.