Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ILGenerator catching exceptions doesn't work

I'm generating wrappers for types by using System.Reflection.Emit. At one point it's possible that the original object is throwing a error on access ( FaultException ) and the error should be catched by my try { } catch (Exception e) { } which i have implemented, but it does not.

The code is shown correcly by ILSpy.

try
{
    if (original.Station != null)
    {
        if (objectDictionary.ContainsKey(original.Station))
        {
            this.Station = (objectDictionary[original.Station] as StationWrapper);
        }
        else
        {
            this.Station = new StationWrapper(original.Station, objectDictionary);
        }
    }
}
catch (Exception arg_6D_0)
{
    ReportManager.Log(arg_6D_0);
}

Code generation

This is the code for the assembly-generation.

Label ex = il.BeginExceptionBlock();
....
// Exception block end
il.Emit(OpCodes.Leave, ex);
il.BeginCatchBlock(typeof(Exception));
il.Emit(OpCodes.Call, ReportManager_Log);
il.EndExceptionBlock();

Edit

The exception is getting caught by user code but not by IL-Code.

Disassembly

Removed some namespaces of the customer here. The write line has been added the last minutes.

.try
{
    IL_0019: ldarg.1
    IL_001a: call instance class [...]...Station [...]...StationBase::get_Station()
    IL_001f: brfalse IL_0063

    IL_0024: ldarg.2
    IL_0025: ldarg.1
    IL_0026: call instance class [...]...Station [...]...StationBase::get_Station()
    IL_002b: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2<object, object>::ContainsKey(!0)
    IL_0030: brfalse IL_0051

    IL_0035: ldarg.0
    IL_0036: ldarg.2
    IL_0037: ldarg.1
    IL_0038: call instance class [...]...Station [...]...StationBase::get_Station()
    IL_003d: call instance !1 class [mscorlib]System.Collections.Generic.Dictionary`2<object, object>::get_Item(!0)
    IL_0042: isinst ...StationWrapper
    IL_0047: call instance void ...StationBaseWrapper::set_Station(class ...StationWrapper)
    IL_004c: br IL_0063

    IL_0051: ldarg.0
    IL_0052: ldarg.1
    IL_0053: call instance class [...]...Station [...]...StationBase::get_Station()
    IL_0058: ldarg.2
    IL_0059: newobj instance void ....StationWrapper::.ctor(class [...]...Station, class [mscorlib]System.Collections.Generic.Dictionary`2<object, object>)
    IL_005e: call instance void ...StationBaseWrapper::set_Station(class ...StationWrapper)

    IL_0063: leave IL_007c
} // end .try
catch [mscorlib]System.Exception
{
    IL_0068: ldstr "Its comming home"
    IL_006d: call void [mscorlib]System.Console::WriteLine(string)
    IL_0072: call void [...Report]...ReportManager::Log(class [mscorlib]System.Exception)
    IL_0077: leave IL_007c
} // end handler

Edit 2

When throwing a System.Exception in IL code, before the FaultException'1 can occur, the exception is getting handled. Tested with Exception and ArgumentException.

like image 325
Felix K. Avatar asked Feb 21 '12 09:02

Felix K.


1 Answers

All works fine here; are you sure it isn't a nested exception block?

Example:

using System;
using System.Reflection.Emit;

public class Test
{
    static void Main()
    {
        var dm = new DynamicMethod("foo", null, new[] {typeof(bool)});
        var il = dm.GetILGenerator();

        Label ex = il.BeginExceptionBlock();
        il.Emit(OpCodes.Ldarg_0);
        il.EmitCall(OpCodes.Call, typeof(Test).GetMethod("Throw"), null);
        il.Emit(OpCodes.Leave, ex);

        il.BeginCatchBlock(typeof(Exception));
        il.EmitCall(OpCodes.Call, typeof(Test).GetMethod("Log"), null);
        il.EndExceptionBlock();
        il.Emit(OpCodes.Ldstr, "done");
        il.EmitCall(OpCodes.Call, typeof(Console).GetMethod("WriteLine",
                      new[] {typeof(string)}), null);

        il.Emit(OpCodes.Ret);
        var act = (Action<bool>)dm.CreateDelegate(typeof (Action<bool>));
        Console.WriteLine("Expect success:");
        act(false);
        Console.WriteLine("Expect fail:");
        act(true);
        Console.WriteLine("(all done)");
    }
    public static void Throw(bool fatal)
    {
        if(fatal) throw new InvalidOperationException("Boom!");
    }
    public static void Log(Exception ex)
    {
        Console.WriteLine("Error: " + ex.Message);
    }
}
like image 178
Marc Gravell Avatar answered Nov 02 '22 15:11

Marc Gravell