Dynamic IL method causes “Operation could destabil

2019-06-26 02:55发布

问题:

System.Security.VerificationException: Operation could destabilize the runtime. at Connance.CommunicatorApi.ReportApiClient.AcknowledgeRecallsAsyncDynamicHandler(Object , AcknowledgeRecallsCompletedEventArgs )

That's the error I'm getting. What I'm trying to do (background) is create a global event handler for a class of methods. I'm working with a Static Proxy in WCF and I need to create a layer which tracks all the calls and returns to all of the WCF web methods. Unfortunately, WCF strongly types the "Completed" events' EventArgs, making it nearly impossible.

I decided to try something. If an event is EventHandler<SomeSpecificEventArgs>, I can still register a method of signature void Method(object, object) to handle the event. Great. So I set off to create a DynamicMethod which would call my global handler, and register it to each event.

I tried two ways:

1) DynamicMethod is of type void (object, object)

2) of type void (object, SomeSpecificEventArgs) -- I use a generic method for this to get the type.

Only, when I try to invoke the method, either manually or for the event, I get the above exception.

Here's my code:

    // The handler for all callbacks.
// in the example it does nothing.
public void Handler(object sender, object e)
{
    dynamic evtArgs = e;
    object userState = evtArgs.UserState;
}

private string GetIdentifier(Delegate d)
{
    return string.Concat(d.Method.DeclaringType, '.', d.Method.Name);
}

// Method to register an event handler
public void Register<T> (Delegate o) where T : EventArgs
{
    // get some info
    /* snip. code to get method name, and calculate name of event */

    var eventInst = ownerType.GetEvent(eventName);

    // The following works, for example:
    // someObj.MethodCompleted += Handler;
    // even though MethodCompleted is an event of type EventHandler<SomeSpecialEventArgs>

    // get the actual type of handler
    var handlerType = eventInst.EventHandlerType;
    EventHandler evtHandler = new EventHandler(Handler);    

    DynamicMethod dm = new DynamicMethod(
        GetIdentifier(o) + "DynamicHandler", // set the name
        typeof(void),                        // return void
        new[] { typeof(object), typeof(T) });// params object and type of event args

    ILGenerator gen = dm.GetILGenerator();

    gen.Emit(OpCodes.Ldarg_0); // load first arg to stack for calling
    gen.Emit(OpCodes.Ldarg_2); // load second arg to stack for calling

    gen.Emit(OpCodes.Call, evtHandler.Method); // call method

    gen.Emit(OpCodes.Ret); // return

    // this is the final delegate
    var superdlg = dm.CreateDelegate(handlerType);

    // the problem beings here:
    // when the event is raised and the delegate is invoked
    // of if I dynamicInvoke it, I get the error
    eventInst.AddEventHandler(ownerInst, superdlg);
}

edit: I see. It turns out I have another issue. I'm working in Silverlight. I managed to reproduce my scenario in a separate project and I got it working by using the overload of DynamicMethod which allows you to set an owner. I then specify

DynamicMethod dm = new DynamicMethod("TestMethod2", typeof(void), new[] { typeof(MyClass), typeof(string), typeof(string) }, typeof(MyClass));,

and use ldarg.0, ldarg.1, and ldarg.2. But this is a security critical constructor and won't run on silverlight. I'm just not sure how I need to set it up then. Do I make the Handler public static and load args 0-1? I end up getting an error like this:

Attempt by method 'DynamicClass.TestMethod2(System.String, System.String)' to access method 'dynamicass.MyClass.Handler(System.String, System.String)' failed."}

回答1:

Method arguments are zero indexed - use ldarg.0 and ldarg.1 instead of ldarg.1 and ldarg.2

Theres also a problem with calling the event handler method - you're not specifying the this pointer for the method (Delegate.Target). You need to provide a this pointer, which may or may not be static depending on what is registered.

This also doesn't take care of multicast delegates - this would only call one of the handlers registered on the event. What you need to do is produce a method something like this:

.method public static CallEventHandler(EventHandlerType ev, object sender, EventArgsType e) {
    ldarg.0   // the Invoke 'this' pointer
    ldarg.1
    ldarg.2
    callvirt instance void EventHandlerType::Invoke(object, EventArgsType)
    ret
}

This uses the event's Invoke method, which deals with calling all the registered handlers for you.



回答2:

Ok, so what I came up was with this.

Make the Handler method an instance method, and add another argument type for the DynamicMethod constructor of the type of the class that owns it (for the implicit this argument).

then you do dm.CreateDelegate(_args_, this)