views:

306

answers:

3

I have a VB6 application that uses several components written in .net. The application must shutdown gracefully when windows is shut down. The problem is that if the .net part of the code is displaying a window, the application displays the message "Cannot Quit" and fails to exit. (It is then terminated by the OS)

I've managed to reproduce this in a simplified app.

The .net code creates a WPF window and displays it using ShowDialog()

[Guid("5F3D0B23-2196-4082-B9DE-B208C61FE89F")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IComShutdownTest
{
    [DispId(1)]
    void RunTest();
}

[Guid("E6613EDD-D51B-42c0-AA5B-5961AB28D063")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("ShutdownTest")]
public class ShutdownTest : IComShutdownTest
{
    public ShutdownTest() 
    {  }

    public void RunTest()
    {
        TestWindow testWindow = new TestWindow();
        bool? dialogResult = testWindow.ShowDialog();
    }
}

As you can see the .net call blocks (and is on the GUI thread) and I suspect this may be the root of the problem, but I can't go round making all my calls non-blocking. I would have assumed that when the OS shutsdown, that the all of the open application windows are terminated.

The VB6 application loads and displays the .net form from a button click.

Private Sub ButtonTest_Click()

    LogEventToFile "Starting"
    Dim dotNetTestObject As ShutdownTest

    LogEventToFile "Creating"
    Set dotNetTestObject = New ShutdownTest

    LogEventToFile "Running"
    dotNetTestObject.RunTest

    LogEventToFile "Done"

End Sub

If you attempt to shutdown the PC while the .net form is on the screen it fails. The "Cannot quit" message box looks like this.

To recreate this you must mark the assembly as com visible (in the assemblyinfo.cs)

[assembly: ComVisible(true)]

and you must set the Project->Properties>Build tab to "Register for COM interop"

I also registered the compiled assembly with:

regasm ShutdownTestLibrary.dll /tlb ShutdownTestLibrary.tlb

Has anyone encountered this before and have any suggested solutions.

+1  A: 

If you call ShowDialog() on any window, it blocks on that thread until the window closes (from the user or from code).

You will probably have to make a method on your COM interface to close the window in .Net, or somehow get the window's handle in VB to close the window.

Jonathan.Peppers
But shouldn't the OS shutdown close all the open windows. (I thought it sent the QuerySessionEnd and SessionEnd messages to all open windows which would cause them to close). If you make a pure .net app with 2 windows and use ShowDialog() it will still close automatically when the OS shuts down.
Simon P Stevens
The OS sends this command to all top-level windows, and they in turn close their children. Since your top-level window is VB, it probably does not know how to close your .Net window. It just knows it's main thread is blocked.
Jonathan.Peppers
A: 

One of the following should solve your issue:

  • Make sure your .Net window has the Text/Name properties set, I know this causes issues like this sometimes.
  • Set the owner of the .Net window to your VB window, use the SetParent function in user32.dll
  • Hook into Microsoft.Win32.SystemEvents.SessionEnding in your .Net library and close the window.
Jonathan.Peppers
A: 

You could try to take the parent window as parameter to the .Net method, that way the .NET component gets notified when the parent tries to close.

public void RunTest(IWin32Window owner)
{
    TestWindow testWindow = new TestWindow();
    bool? dialogResult = testWindow.ShowDialog(owner);
}

And in your VB6 code:

Call dotNetTestObject.RunTest(Me.hWnd)
awe