tags:

views:

222

answers:

3

I am developing a .NET 2.0 plug-in based application. My application detects/loads plug-ins at run-time via a System.Reflection inspection of other .NET assemblies in a specified directory. This works great. My application contains a PropertyGrid control populated from [Browsable(true)] properties present in the loaded plug-ins. In this PropertyGrid, browsable-true-properties exhibit the following behavior:

  • Properties of basic/primitive types (bool, string, etc.) load and cleanup properly
  • Properties of user-defined types (like a plug-in side defined enum) load properly and cleanup properly when the user does not modify then at run-time.
  • If a user modifies a non-standard type at run-time (i.e. changes the value of an enum via the PropertyGrid) the application hangs upon closing. This is my problem.

Using Visual Studio .NET 2005 and Red Gate's Reflector, I was able to isolate the hang to the following segment of code from Microsoft.Win32.SystemEvents.WindowThreadProc (I was working from the raw assembly, but I am 99% sure this is the right place):

 while (flag)
 {
     if (UnsafeNativeMethods.MsgWaitForMultipleObjectsEx(0, IntPtr.Zero, 100, 0xff, 4) != 0x102)
     {
          goto Label_0072;
     }
     Thread.Sleep(1);
     continue;
    Label_0053:
     if (msg.message == 0x12)
     {
         flag = false;
         continue;
     }
     UnsafeNativeMethods.TranslateMessage(ref msg);
     UnsafeNativeMethods.DispatchMessage(ref msg);
    Label_0072:
    if (UnsafeNativeMethods.PeekMessage(ref msg, NativeMethods.NullHandleRef, 0, 0, 1))
    {
        goto Label_0053;
    }
}

It appears 'flag' is not being set to true, hence my program sits in this loop forever. I found someone with a similar problem at .NET 247, but his recommended workaround:

System.Threading.Thread.CurrentThread.SetApartmentState(Threading.ApartmentState.STA)

didn't seem to fix things.

Any thoughts?

Thanks in advance.

+3  A: 

Make sure your entry point for your application is flagged [STAThread] - the STAThreadAttribute is the only way in .NET 2+ to mark your UI thread as STA. Setting the ApartmentState after the thread's started (which worked in 1.1) is not valid guidance anymore.

This should look like:

public class Program
{
    [STAThread]
    static void Main() 
    {
        Application.EnableVisualStyles();
        Application.Run(new MyMainForm());
    }
}
Reed Copsey
Thank you! I was able to fix the issue. It was slightly more complicated than your snippet indicates, but your post put me on the right track. The complications stemmed from the fact that this code was written in VB.net (ugh -- not my decision) so, per usual, VB.net tried to make everything easy by hiding code (which ends up making things hard). I eventually created my own "Starter" class and manually edited the .vbproj file in Textpad to use it. By default, VS wanted to use the My.Application class which was a pain to work with. See below for a full snippet of Starter.vb
Vincent
That's basically the same thing I was saying. The trick is adding <STAThread()> (in VB.NET).
Reed Copsey
Absolutely -- I'm just not used to VB.net and had trouble *finding* Sub Main() which is not as easy to find as it would be in C#. Thanks again!
Vincent
A: 

Lookst like you've isolated the problem incorrectly. The code that you've posted is for a window procedure for an invisible window used by WinForms to catch system window messages (like WM_SETTINGCHANGE). It's run on a thread different from your main thread, so it shouldn't affect it in any way. If you have just attached the debugger to your process, most likely you've got the wrong thread.

The flag here is actually set when window gets WM_QUIT. However, this shouldn't matter, because the thread is also created as a background thread, meaning that it will be killed anyway when your main thread terminates - so even if the flag isn't set and it keeps looping, it still won't hang the app on exit.

(all of the above is easy to find out if you look at .NET source code available from MS debugging source servers if you use VS2008 SP1).

Pavel Minaev
A: 

For the reference of anyone who stumbles across this in the future:

Friend Class Starter

<STAThread()> _
Shared Sub Main()
    Application.EnableVisualStyles()

    Dim client As ClientGUI
    client = New ClientGUI()
    Application.Run(client)

    My.Settings.Save()
End Sub
End Class
Vincent
I suspect this would have been MUCH easier in C# as Reed's snippet could have been used directly. VB.net forms try to be clever using My.Application as the startup object, but the problem is that seems to have a "hidden" Sub Main() already, so you can't just add your own.
Vincent