2

This relates to a VB6 app that shows a C# form via interop.

An event in the C# form causes one of the VB6 app forms to show.

Usually, when this VB6 form is made to be hidden (Form.Hide) the underlying C# form is brought to the front.

But if during its life-time the VB6 form has caused a MsgBox to be shown, then the underlying C# form will not be at the front when the VB6 form is made to be hidden.

Why does this happen? It's like the MsgBox is changing the Z-Order of the forms.

CJ7
  • 22,579
  • 65
  • 193
  • 321

2 Answers2

1

"How do I cause the C# form to show after the VB6 is hidden? Do I have to use window handles?"

Assuming you are ok with the orphaned msgbox being kept open. When the VB6 form is Hidden you need to fire an event to get the Window Handle:

public static int FindWindow(string windowName, bool wait)
{
    int hWnd = FindWindow(null, windowName);
    while (wait && hWnd == 0)
    {
         System.Threading.Thread.Sleep(500);
         hWnd = FindWindow(null, windowName);
    }

    return hWnd;
}

Then bring the C# window to the Top:

[DllImport("user32.dll", SetLastError = true)]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

// When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter
[DllImport("user32.dll")]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);

[DllImport("kernel32.dll")]
public static extern uint GetCurrentThreadId();

/// <summary>The GetForegroundWindow function returns a handle to the foreground window.</summary>
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();

[DllImport("user32.dll")]
public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);

[DllImport("user32.dll", SetLastError = true)]
public static extern bool BringWindowToTop(IntPtr hWnd);

[DllImport("user32.dll", SetLastError = true)]
public static extern bool BringWindowToTop(HandleRef hWnd);

[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);

private static void ForceForegroundWindow(IntPtr hWnd)
{
    uint foreThread = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
    uint appThread = GetCurrentThreadId();
    const uint SW_SHOW = 5;

    if (foreThread != appThread)
    {
        AttachThreadInput(foreThread, appThread, true);
        BringWindowToTop(hWnd);
        ShowWindow(hWnd, SW_SHOW);
        AttachThreadInput(foreThread, appThread, false);
    }
    else
    {
        BringWindowToTop(hWnd);
        ShowWindow(hWnd, SW_SHOW);
    }
}

Ref: SetForegroundWindow Win32-API not always works on Windows-7

Jeremy Thompson
  • 61,933
  • 36
  • 195
  • 321
  • What about the `NativeWindow` class found down the bottom here: http://social.msdn.microsoft.com/Forums/lt/vbinterop/thread/2692df26-317c-4415-816b-d08fe6854df8 – CJ7 Sep 23 '12 at 16:27
  • +1 Be aware that Windows guru Raymond Chen points out that this `AttachThreadInput` workaround [can cause your program to stop responding](http://stackoverflow.com/a/8081858/15639) in some circumstances. However I've never encountered these bugs myself. YMMV. – MarkJ Sep 25 '12 at 12:05
  • @MarkJ: what about the approach taken in my comment above? – CJ7 Sep 25 '12 at 12:06
  • @CJ7 I saw that, what were the results? ps this [VB6 Froms contain c# forms](http://stackoverflow.com/questions/4953725/contain-a-vb6-form-in-a-net-mdi) and vice versa is why I wish to know. – Jeremy Thompson Sep 25 '12 at 12:07
  • 1
    @CJ7 Sounds promising. It doesn't use `AttachThreadInput` so Raymond's warnings don't apply. Let us know if it works - if it does work post it as an answer to your own question. – MarkJ Sep 25 '12 at 12:12
  • @CJ7 +1 for your answer – MarkJ Feb 18 '14 at 11:53
1

I got it to work by using the NativeWindow class, following the last answer in this thread: http://social.msdn.microsoft.com/Forums/en-US/2692df26-317c-4415-816b-d08fe6854df8/vbnet-vb6-win32-api-problems?forum=vbinterop

That code uses FindWindowEx to get the handle of the VB6 window, which is unecessary because you can simply pass the handle of the VB6 window to the .NET form:

public void ShowDotNetForm(IntPtr hwndMain) 
{
    NativeWindow vb6Window = new NativeWindow();
    vb6Window.AssignHandle(hwndMain);
    f.Show(vb6Window);
}

The code in the VB6 form is:

dotNetObj.ShowDotNetForm Me.hWnd

Passing the window handle from VB6 is better because FindWindowEx requires you to know the text of the window title to get the handle.

CJ7
  • 22,579
  • 65
  • 193
  • 321