locked
Handling exceptions in reflected assemblies? RRS feed

  • Question

  • Hi,

    Sorry if this doesn't make sense - I'm getting myself confused :) I'm using reflection to load assemblies from dll's at runtime but I'm having a bit of trouble catching any exceptions it throws except in Main(), which isn't of much use as it means the application exits. Is it possible to catch these exceptions elsewhere without having to exit the application?

    I realise that if I was invoking the methods etc. directly myself I could wrap it in a try...catch block, but that doesn't seem to fit with my scenario properly. This is what happens:
    1. Application starts
    2. Load the assembly from the dll
    3. Calls CreateInstance on a type implementing a known interface
    4. Runs a Load function on the interface
    5. The Load function uses another interface to add a menu item to the main application and attach an event
    6. The event handler for the menu creates an instance of a form class within the assembly and displays it
    If I add a button to the form that throws an exception I don't seem to be able to catch it anywhere except using a try...catch around Application.Run line in Main(). Even looking at the stack trace it's not obvious exactly where that exception comes from, unless I'm just missing something obvious lol!

    Thanks
    Monday, July 27, 2009 7:07 PM

Answers

  • What I'd like to do in this instance (or indeed on any exception thrown by an reflected assembly) is to get rid of the assembly but let my app continue - can I assume that this is impossible?
    That is not impossible, but it is difficult.

    The way to accomplish this is to load the assembly into a separate appdomain.  When the assembly "dies", it will tear down the entire app domain that contains it - by keeping this in a separate AppDomain, you can insulate your main application from this occurring.

    A good intro to AppDomains was written by Chris Brumme.  Security concerns are one of the main benefits of using them.

    Reed Copsey, Jr. - http://reedcopsey.com
    • Marked as answer by TBA-UK Monday, July 27, 2009 9:03 PM
    Monday, July 27, 2009 8:44 PM

All replies

  • Showing the exact error/stacktrace might help us, though, even if you don't understand it.  Please post it here.


    David Morton - http://blog.davemorton.net/ - @davidmmorton - ForumsBrowser, a WPF MSDN Forums Client
    Monday, July 27, 2009 7:09 PM
  • For this exception I've used the System.Net.NetworkInformation.Ping asynchronously, and close the form before the callback happens. I know why the exception is occuring, just not how to catch it from the 'host' application

    System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.ObjectDisposedException: Cannot access a disposed object.
    Object name: 'RichTextBox'.
       at System.Windows.Forms.Control.CreateHandle()
       at System.Windows.Forms.TextBoxBase.CreateHandle()
       at System.Windows.Forms.Control.get_Handle()
       at System.Windows.Forms.RichTextBox.get_TextLength()
       at System.Windows.Forms.TextBoxBase.AdjustSelectionStartAndEnd(Int32 selStart, Int32 selLength, Int32& start, Int32& end, Int32 textLen)
       at System.Windows.Forms.TextBoxBase.GetSelectionStartAndLength(Int32& start, Int32& length)
       at System.Windows.Forms.TextBoxBase.AppendText(String text)
       at STL.Ping.ShowPingResults(PingReply pingResponse) in E:\STL\Ping.cs:line 129
       at STL.Ping.ping_PingCompleted(Object sender, PingCompletedEventArgs e) in E:\STL\Ping.cs:line 88
       at System.Net.NetworkInformation.Ping.OnPingCompleted(PingCompletedEventArgs e)
       at System.Net.NetworkInformation.Ping.PingCompletedWaitCallback(Object operationState)
       --- End of inner exception stack trace ---
       at System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
       at System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
       at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
       at System.Delegate.DynamicInvokeImpl(Object[] args)
       at System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme)
       at System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj)
       at System.Threading.ExecutionContext.runTryCode(Object userData)
       at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
       at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry tme)
       at System.Windows.Forms.Control.InvokeMarshaledCallbacks()
       at System.Windows.Forms.Control.WndProc(Message& m)
       at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
       at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
       at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
       at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
       at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
       at System.Windows.Forms.Application.Run(Form mainForm)
       at ServerToolbox.Program.Main() in E:\STL\Program.cs:line 21

    thanks

    edit: I realise its bad coding on the part of the reflected assembly :) but I won't always be able to control the code thats being run, so would just like to avoid the application terminating
    • Edited by TBA-UK Monday, July 27, 2009 7:18 PM
    Monday, July 27, 2009 7:14 PM
  • You need to make sure you remove the event callback to the Ping, or else abort the ping, before closing the window.  You can do this in the OnClosing override within the form, or you can handle the FormClosing event and abort the ping from there.

    If you're using SendAsync, call SendAsyncCancel.  If you've handled one of the events such as PingCompleted, make sure you remove the method from the event handler by using the -= operator.


    David Morton - http://blog.davemorton.net/ - @davidmmorton - ForumsBrowser, a WPF MSDN Forums Client
    • Proposed as answer by JohnGrove Monday, July 27, 2009 8:28 PM
    Monday, July 27, 2009 7:18 PM
  • Thanks for the info - you might not have seen what I added because it was a bit of a late edit, but I realise I can fix the problem in the assembly that I'm loading, but as I wont be able to do much about the code thats being run I'd like to catch the exception, display it and let my application continue regardless of what happens inside the reflected assembly

    can this be done, or would I need to start a new thread and load the assembly there? doable, but a fair bit more complex ;(
    Monday, July 27, 2009 7:21 PM
  • The stack trace is pretty sad.  It is accessing the text box on a thread pool thread, quite illegal.  That happens because the sync object is dead, the Handle is null because the form was closed.  InvokeRequired returned false.  You're kinda lucky that it crashed on ObjectDisposed, nothing good would happen if it did manage to avoid that exception.  Note that it is trying to resurrect the form.

    You can catch the exception it in your ShowPingResults() method.  But really, you have to shut down the ping before closing the form.

    Hans Passant.
    • Proposed as answer by JohnGrove Monday, July 27, 2009 8:28 PM
    Monday, July 27, 2009 8:26 PM
  • Thank you, so to summarise, if I'm loading unreferenced assemblies using reflection then I must rely on the assemblies handling their own exceptions, and any exception that does reach my application will cause it to exit?

    What I'd like to do in this instance (or indeed on any exception thrown by an reflected assembly) is to get rid of the assembly but let my app continue - can I assume that this is impossible?

    Out of interest, how does Office (for example) handle this situation when loading addins? If an addin in Office throws an exception would it cause the Office app to exit as it does with mine? If not, then presumably there must be a workaround :)

    thanks
    Monday, July 27, 2009 8:35 PM
  • What I'd like to do in this instance (or indeed on any exception thrown by an reflected assembly) is to get rid of the assembly but let my app continue - can I assume that this is impossible?
    That is not impossible, but it is difficult.

    The way to accomplish this is to load the assembly into a separate appdomain.  When the assembly "dies", it will tear down the entire app domain that contains it - by keeping this in a separate AppDomain, you can insulate your main application from this occurring.

    A good intro to AppDomains was written by Chris Brumme.  Security concerns are one of the main benefits of using them.

    Reed Copsey, Jr. - http://reedcopsey.com
    • Marked as answer by TBA-UK Monday, July 27, 2009 9:03 PM
    Monday, July 27, 2009 8:44 PM
  • Brilliant, thank you!! Will have to do a lot of reading on AppDomains, sounds like the answer I'm looking for. Had a quick read through that article, interesting stuff :)

    edit:
    for anyone else trying to achieve the same thing, I've just come across some useful info in these two links:
    http://www.code-magazine.com/article.aspx?quickid=0211081&page=3
    http://stackoverflow.com/questions/225330/how-to-load-a-net-assembly-for-reflection-operations-and-subsequently-unload-it
    • Edited by TBA-UK Monday, July 27, 2009 9:07 PM
    Monday, July 27, 2009 9:03 PM