views:

228

answers:

2

I have a static class that creates several worker threads in its constructor. If an exception occurs before the workers have been created my Application.ThreadException handler (used to shut the app down if an error not known to be recoverable occurs) triggers normally and everything is fine. Once the first worker thread has been created however in addition to the handler firing I get an "MYAPP has encountered a problem and needs to close. We are sorry for the inconvenience." dialog for MS error reporting.

In this specific instance I can reorder the code to create the threads last (after any resource initialization/access problems that could've triggered an exception) but that's no more than a bandaid to the problem and doesn't give me any information about what's actually going on.

Hopefully I've excised enough code from my app to show what I'm trying to do here.

class Program
{
 /// <summary>
 /// The main entry point for the application.
 /// </summary>
 [STAThread]
 static void Main(string[] args)
 {
  try
  {
   Application.Run(theForm);
   theForm.Dispose();

  }
  catch (Exception e)
  {
   //doing this to use the same handler here and for Application.ThreadException
   ThreadExceptionEventArgs argsEx = new ThreadExceptionEventArgs(e);
   FatalExceptionHandler(null, argsEx);

  }
  finally
  {
   MyStaticClass.KillThreads();
  }
 }

 public static void FatalExceptionHandler(object sender, System.Threading.ThreadExceptionEventArgs ex)
 {
  Exception e = ex.Exception;
  try
  {
   //lots of stuff to give more useful error messages for known problems
   //and display them in a messagebox.
  }
  // if anything went wrong scraping the exception text for formatting, show the raw value.
  catch
  {
   MessageBox.Show(e.Message);
   return;
  }
  // after showing the MessageBox, close out the app.
  finally
  {
   System.Environment.Exit(1);
  }
 }
}

class MyStaticClass
{
 static MyStaticClass()
 {
  myThread = new Thread(new ThreadStart(SomeMethod));

  //if this exception is thrown everything works normally
  //Throw new Exception("KABOOM");

  myThread.Start();

  //if this exception is thrown a windows error reporting dialog appears 
  //along with the messagebox from program.FatalExcetion handlder    
  //Throw new Exception("KABOOM");
 }


 public void KillThreads()
 {
  //clean up the worker threads
 }
}
+4  A: 

Static constructors are not called at a specific time during application startup. In particular, if you never reference MyStaticClass from another class, it may never be initialized. Since you can't reason about this in any sane way, you should provide a StaticInitialize() method (or similar) for the types with non-trivial code in the static constructor plus cases where the static initialization code really needs to run. Move the code from the static constructor to the static initialization method.

280Z28
I thought the constructor was always called, at the latest, when a member/method/property of it was accessed.
Dan Neely
That's correct, but that an "if and when" the member is accessed, and it still doesn't address a) which thread the constructor runs on and b) when it runs relative to application lifetime. Here's a rule-of-thumb I just made up that could help you: if the code in the static constructor couldn't run twice in a row without changing the program's behavior, move the code to a static initialization method.
280Z28
If I move all the code from the static constructor to an initialize method is there any reason to keep the constructor around?
Dan Neely
If you have a `private static readonly` member field, such as a `List<T>` or `Dictionary<T,K>` you'll need to initialize it in a static constructor.
280Z28
+1  A: 

The Application.ThreadException is only raised for UI thread unhandled exceptions (since is part of the Application class). The worker thread unhandled exception event is AppDomain.UnhandledException. When an unhandled exception on an worker thread occurs this event is raise and then the system crash dialog is displayed. The AppDomain.UnhandledException is intended for logging purposes only and there is no documented way to prevent the system crash error dialog from being shown.

Remus Rusanu
In both cases the exception was called in the same method, and should be coming from the same thread as a result, so I don't understand how AppDomain.UnhandledException is getting involved.
Dan Neely
Also, is there a way i can attach a blanket exception catcher on my worker threads to keep anything from going to AppDomain.UnhandledException?
Dan Neely
I think that the differenc ein behavior between the two places where you throw can be attributted to timing difference. On one case the static constructor is run y the UI thread and hence is cought by the application umbrella handler, on the other case due to different timing the constructor is actually run by a worker thread, so it goes to the appdomain non-ui case. Unfortunately there is no global try-catch for non-ui threads.
Remus Rusanu
The exception stack should be able to validate if I'm right or wrong about the timing. Check what's at the bottom of the stack, the app main entry point of the worker thread start.
Remus Rusanu
The exception being called after myThread.Start() is called is has a stack trace indicating that it come from the worker thread and the second (of 2) elements in the trace is myStaticClass.SomeMethod(). But since the MyStaticClass constructor is being called from the main thread and the exception is being thrown in MyStaticClass's constructor how can it be ending up in the worker thread?
Dan Neely
The worker thread is referencing your class. Since the static ctor is not finished on the UI thread, the CLR runs it again from the worker thread. Static constructors are not thread safe, see http://msdn.microsoft.com/en-us/library/ms998558.aspx
Remus Rusanu
I didn't know that. Thank you.
Dan Neely