WindowsMobile: Application Exits after handling Ex

2019-01-15 17:24发布

问题:

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:

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.



回答2:

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.



回答3:

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.



回答4:

https://connect.microsoft.com/VisualStudio/feedback/details/94356/modal-windows-in-net-compact-framework-application-stop-working-if-unhandled-exception-on-modal-dialog-is-raised#tabs

In compact framework 2.0 this is a accepted issue... they dont care...

If you use a showdialog inside a try once you have catch an exception the showdialod does not work anymore....



回答5:

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";
}


回答6:

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.



回答7:

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?



回答8:

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