c# - Unhandled exception coming from GC thread when a static-constructor / type-initializer fails -


(title was: "typeloadexception not wrapped targetinvocationexception reflection used")

with bltoolkit figured out interesting fact - methodinfo.invoke not catching exception in calling method.

see example - emulates exception in static constructor method, invoking via reflection.

problem testcomponent inherits component , have overridden dispose method. in sample 2 messages - 1 "handle" , 1 "unhandle" - seems components have different handling inside reflection on lower levels.

if comment out method dispose(bool disposing) - receive "handle" message.

can give explanation why happen , propose solution? try-catch inside bltoolkit cannot marked answer - not member of team :)

    class program {     static void main()     {         appdomain.currentdomain.unhandledexception +=             (sender, eventargs) => console.writeline("unhandled " + eventargs.exceptionobject.gettype().fullname);         try         {             try             {                 var instance = activator.createinstance(typeof(componentexecutor));                 methodinfo mi = typeof(componentexecutor).getmethod("do");                 bindingflags bf = bindingflags.public | bindingflags.instance | bindingflags.declaredonly |                                   bindingflags.invokemethod;                  mi.invoke(instance, bf, null, new object[0], cultureinfo.invariantculture);             }             catch (targetinvocationexception tarex)             {                 throw tarex.innerexception;             }         }         catch (exception ex)         {             console.writeline("handled " + ex.gettype().fullname);         }     }      class componentexecutor     {         public void do()         {             new testcomponent().do();         }     }     class testcomponent : component     {         static testcomponent()         {             throw new nullreferenceexception();         }         [methodimpl(methodimploptions.noinlining)]         public void do()         {             console.writeline("doing");         }         protected override void dispose(bool disposing)         {             console.writeline("disposing");             base.dispose(disposing);         }     } } 

this isn't related reflection; similar result just:

    appdomain.currentdomain.unhandledexception +=         (sender, eventargs) => console.writeline("unhandled " + eventargs.exceptionobject.gettype().fullname);     try     {         new testcomponent();     }     catch (exception ex)     {         console.writeline("handled " + ex.gettype().fullname);     }     console.writeline("happy"); 

it writes "happy"; unhandled exception seems coming gc, presumably trying collect partially constructed object (it never calls instance constructor), , falling over... in particular note on different threads (i'm pretty sure unhandled on gc thread). being gc/threaded can pain repro; added gc.collect(gc.maxgeneration, gccollectionmode.forced); locally, force gc happen see more often. fascinating.

since neither instance constructor (testcomponent()) nor finalizer (~testcomponent()) invoked, have no possible way (that can tell) of fixing this.

the main thing can suggest here, sadly, is: don't have type initializers fail :(

the 1 thing can working cheat runtime:

object obj = formatterservices.getuninitializedobject(typeof(testcomponent)); 

this still hits type initializer , fails, object doesn't seem quite lost. maybe route doesn't mark finalization. , gc doesn't hate quite much.

you still going have major problems ever using object if type initializer borked.

so in code, might have:

class componentexecutor {     public void do()     {         using (var tc = (testcomponent)                 formatterservices.getuninitializedobject(typeof(testcomponent)))         {             // call ctor manually             typeof(testcomponent).getconstructor(type.emptytypes).invoke(tc, null);             // maybe can skip since `using`             gc.reregisterforfinalize(tc);              tc.do();         }     } } 

now:

  • when type initializer fails, nothing finalized (it doesn't need - object can't possibly have @ point, since never ran constructor)
  • when type initializer works, ctor executed , registered gc - hunky dory

Comments

Popular posts from this blog

java - SNMP4J General Variable Binding Error -

windows - Python Service Installation - "Could not find PythonClass entry" -

Determine if a XmlNode is empty or null in C#? -