views:

190

answers:

2

I'm trying to do something along these lines:

private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
    try
    { 
        e.Handled = true;

        var errorMessage = BuildErrorMessage(e.ExceptionObject);
        var service = new MyService(Constants.MyBinding, Constants.MyServiceUri);
        service.LogErrorAsync(errorMessage);              
        // wait awhile for the channel to flush its buffers, hard abort if it takes too long
        //service.InnerChannel.Close(new TimeSpan(0, 0, 10));
    }
    finally
    {
        RestartSilverlightApp();
    }
}

It worked at first -- I have the log entries to prove it! -- but wasn't reliable. Tweaked things and now I can't make it work at all. Breakpoints on the server component are never hit; Fiddler shows that the client never even puts an HTTP request on the wire. Things I've tried:

  • Inserted the call to Close(), even with crazy long timeouts. [on the theory the App was restarting before the channel could do its thing] Result: the app always hangs until the timeout expires. I found this interesting -- when WCF is behaving normally, Close() returns almost instantly.
  • Inserted a call to service.InnerChannel.Open() in Application_Startup. [in case something about our state inside the exception handler prevents Created -> Opened WCF channel transitions]
  • Made service a member of the App class and initializing it in my Application_Startup handler. [on the theory that the helper classes I use to generate WCF proxies was torn down / in some sort of bad state by the time Silverlight unwound the AppDomain to the point of the global exception handler]
  • Made service static. [on the theory this wasn't the same instance of App, allowing service to be GC'd]
  • Verified that service.State, service.EndPoint, and any other public properties I could quickly check in the debugger were correct immediately prior to the async call.
  • Verified that service's creating, async invoke, and synchronous Open/Close calls were all happening on the Main (UI) thread, and that the thread ID was constant [no partial/"stealth" teardowns prior to my handler getting called].
  • Moved the call to service.LogErrorAsync() outside of Application_UnhandledException. [in case things were misconfigured elsewhere] It worked flawlessly.

I'm not an expert in the Silverlight application lifecycle/architecture, nor WCF. Ideas I've missed?

A: 

Silly question... If your app just crashed, why attempt something as risky as a webservice call?

The strategy I normally peruse in such occasions is:
1. Log the unhandeled exception in IsoStore.
2. Restart the app.
3. when the app starts up it checks if the IsoStore exception log isn't empty. And if it isn't empty - attempts to send those to a server logger.

Plus, given that you're doing an async call and than restarting the app, you've essentially got a race condition there. which is bad. So either "cold log" it (my original suggestion) or wait until the async call returns/fails and then restart the app.

JustinAngel
I wish there were a way to make synchronous calls in Silverlight, but there isn't. You can't even use a ManualResetEvent.WaitOne() to fake it -- service completion handlers are called on the UI thread, so you'll deadlock. Calling Close() is the best workaround I know of. ///// I like the ISO idea a lot. That's probably what I'll go with...hopefully we don't need to log any IsolatedStorageExceptions :)
Richard Berg
Yeah, you might actually get an IsoStore exception if the user intentionally changed your IsoStore quota to 0MB. so before writing to IsoStore, check you've got a reasonable (5kb?) of storage left.
JustinAngel
+1  A: 

So I don't think your question is silly because I have to try to log exceptions as well during any point of a silverlight application's lifecycle. I'm wondering, however, if WCF could be supplanted here with a raw AJAX style POST to a service that logged your exception. You could tie it to the browser and make the call with the JavaScript engine rather than using the WebClient or something else within the Silverlight ecosystem.

David in Dakota
Good idea -- but I went with Justin's since it was easier to implement.
Richard Berg