views:

762

answers:

7

I have the following simple scenario:

A DialogForm with a Button, the Button_click throws an exception.

A MainForm with a button and a Label, in the click I show a new instance of the DialogForm inside a Catch block.

If I run this setup in regular WinForms, I can catch the Exception as expected.

If I run this in WinMobile (I've tested on WM5 and WM6 Pro) I can see with a Debugger that the Catch block is entered but the Exception keeps propagating up and the App dies.

The code in MainForm looks like this:

try
{
   using (DialogForm frm = new DialogForm())
   {
     DialogResult r = frm.ShowDialog();
     label1.Text = r.ToString();
  }
}
catch (Exception ex)
{
  label1.Text = ex.Message;
}

Edit:

I investigated a little further, with a catch {} block around this code and around Application.Run() and the app still quits.

Apparently it is not a runaway Exception, that is caught and handled just fine. But after this operation it looks like the Application performs an unwanted Exit().

+1  A: 

While programming documentation would state that the following construct is unnecessary I've found that sometimes I must use the following.

try
{
 using (DialogForm frm = new DialogForm())   
 {     
       DialogResult r = frm.ShowDialog();
       label1.Text = r.ToString();  
 }
}
catch (Exception ex)
{
    label1.Text = ex.Message;
}
catch
{
    label1.Text = "Unknown Exception";
}
Joel
I tried that and more, no success
Henk Holterman
A: 

I recall that dot net has a default exception handler which is triggered when the exception crosses a particular boundary. It is this handler that is causing your app to quit. I remember looking at this issue some time back but unfortunately no longer have access to the code. As for the differences in behaviour between .NET and .NET CF this is because the two handle frames differently (maybe frames in CF don't have parents or something like that).

You can hook into this default exception handling and do whatever you what... I think the code I implemented was along these lines: Unhandled Exceptions

CurtainDog
Alas, the Application class in CF has no ThreadException or any other events.
Henk Holterman
+1  A: 

Maybe hooking AppDomain.CurrentDomain.UnhandledException (together with the tip by Joel) solves your exception problem? Might be analogous to this other .NET answer.

I use the following code

    try
    {
        AppDomain.CurrentDomain.UnhandledException +=
            (object sender, UnhandledExceptionEventArgs e) =>
            {
                CrashOn((Exception)e.ExceptionObject, e.IsTerminating);
            };
        var mainWindow = new MainWindow();
        MobileDevice.Hibernate += (sender, e) => { mainWindow.Hibernate(); };
        Application.Run(mainWindow);
    }
    catch (Exception huh)
    {
        CrashOn(huh, false);
    }

in the Program.Main() of my WM6 Professional twitter client. Here the CrashOn() method saves the exception information to disk and starts a crash recovery executable. This then shows information that the client crashed, has an option to mail me the exception information and gives the use the option of either exiting or restarting the client.

peSHIr
A useful example but I have added this and the program doesn't enter CrashOn. Note that I have edited the question and the title.
Henk Holterman
+3  A: 

After tinkering on I found something that works:

try {
  // show Dialog that Throws
}
catch (Exception ex) {
  label1.Text = ex.Message;
  Application.DoEvents();  // this solves it
}

The bounty is still open for anyone who can tell me why the DoEvents() is necessary.

Henk Holterman
did you try my suggestion? I'm curious to know how it might affect the execution.
Erich Mirabal
Mmm, sounds like something multi-threading related then. Do you explicitly use threads? Is there any exception handling in the button click handler in the dialog?
peSHIr
No threading, and there is no (shouldn't be) any handling in the Dialog button. The aim is for the main form to catch all exceptions from the Dialog.
Henk Holterman
Threading would have nothing to do with it. When you call ShowDialog, a separate message pump is set up for that dialog, and everything is handled there. My bet is that exceptions are marshaled back to the UI thread via PostMessage and until something happens to cause that message to be dispatched
ctacke
(cont'd) then the exception isn't "handled". DoEvents simply wraps teh call to Translate and DispatchMessage, which forces the system to pop the top item off the pump (your exception) and handle it.
ctacke
Could you consolidate this so tha anyone else with the problem can find the solution quickly.
Stevo3000
Cant believe that I didn't see this solution earlier. It bothered me for weeks. Thanks.
leiz
+1  A: 

The best info I can find on this is the fact that you are calling ShowDialog().

I tried using Reflector, but it doesn't handle the CF dlls. So the best comparison is to look at the main non-CF file. In it, if you trace the ShowDialog() function, it ends up calling Application.RunDialog(form). In there, it creates a new thread context and runs its own message loop for the dialog window.

Since I can't look into the actual CF DLL, I can only take an "educated guess." It would seem that it has to do with the fact that the exception happens on a separate message loop and will not be correctly be caught unless you process the Application's message queue via DoEvents().

So basically, ShowDialog() causes the exception to be created on a separate message loop and the fact that it is caught does not get correctly processed unless you call DoEvents().

What happens if you try a regular Show()? Do you still need DoEvents? I don't have an environment to test this, so I can only suggest that you try it. If you change it to a Show() and no longer need DoEvents(), I think that we would then know for sure whether or not if I was right.

Erich Mirabal
If I do a regular Show I can't surround it with a try/catch, nor can I wait for the response. Exception handling is radically different for Show. I didn' try this yet but I fully expect the application to quit, as it should.
Henk Holterman
A: 

Based on the other answers and my comment regarding the needed DoEvents: Have you tried to check in your original catch statement whether label1.InvokeRequired might be true?

If so, you might have some kind of multi-thread related race condition here because you are setting label1.Text to a property of an exception class thrown from another thread?

In regular .NET 1.0 this often just worked while it shouldn't. In later versions ignoring InvokeRequired == true results in an exception. I'm not sure how the Compact Framework handles such "transgressions". Maybe it just exists the process then?

peSHIr
A useful suggestion but it's not the answer, InvokeRequired is false in the catch block. I have reduced the problem to the very small app described in the question, no Threading etc. Just 2 Forms and 2 Buttons.
Henk Holterman
+2  A: 

The reason you need 'DoEvents' is that this clears the message que while the form is still available.

What is happening is that there are still messages waiting to be processed against the form throwing the exception. By calling 'DoEvents' here you are alowing them to be processed before the using block cleans up the form and stops the messages on the que from being processed.

Stevo3000
You're probably right about the message queue, but the DoEvents is outside of the using-block.
Henk Holterman
It only looks like it's outside of the using block. The catch block will be evaluated before the form object is cleaned up. This is logical as you may need to evaluate an object in the using block from the catch block.
Stevo3000
@Stevo3000 - yep order of execution is as we declare it try then catches then finally. A using expands to a try finally - so you get that behavior. Regardless, I doubt you would see this on Win32. A CF bug perhaps?
Jonathan C Dickinson
@Jonathan - It is most likely down to the way in which the fundamentals of the message pump work in CF rather than an actual bug.
Stevo3000