Dynamic IL method causes "Operation could destabilize the runtime" - c#

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."}

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.

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)

Related

NullReferenceException thrown but the object passed the null check, how is that possible?

I'm using the AddEventHandler method from that answer, but when doing it on an EventHandler with a value type argument that happens:
using System.Reflection;
public class Program
{
public static event EventHandler<bool> MyEvent;
public static void Main()
{
EventInfo eventInfo = typeof(Program).GetEvent(nameof(MyEvent));
AddEventHandler(eventInfo, null, (s, e) => {
if (e == null) return; // either if condition or null conditional operator
Console.WriteLine(e?.ToString());
});
MyEvent(null, true);
}
public static void AddEventHandler(EventInfo eventInfo, object client, EventHandler handler)
{
object eventInfoHandler = eventInfo.EventHandlerType
.GetConstructor(new[] { typeof(object), typeof(IntPtr) })
.Invoke(new[] { handler.Target, handler.Method.MethodHandle.GetFunctionPointer() });
eventInfo.AddEventHandler(client, (Delegate)eventInfoHandler);
}
}
Any explanation?
You are using undocumented, internal api, and what's even worse is that this api accepts raw pointer. So it's not surprising if things go (horribly) wrong if you misuse such api (and you cannot ever be sure you are using it correctly because it's not documented).
Note that AddEventHandler third parameter is EventHandler, which is delegate of this type:
delegate void EventHandler(object sender, EventArgs e);
And your MyEvent delegate type is:
delegate void EventHandler(object sender, int e);
AddEventHandler uses internal undocumented compiler-generated constructor of delegate which accepts two parameters: delegate target and raw pointer to method. It then just passes raw pointer to the method of handler delegate to that constructor without doing any checks. Delegate you pass and delegate being created can be completely incompatible, but you won't notice that until runtime will try to invoke it.
In this case, runtime thinks it has delegate pointing to method void (object, bool), but actually it points to method void (object, EventArgs). You call it via MyEvent(null, true) and runtime passes true boolean value as second argument (it's value type so value is passed directly), but your delegate actually points to method which expects EventArgs, which is a reference type. So it expects an address of some object of type EventArgs. It gets boolean value as if it was pointer to EventArgs.
Now, == null in general case basically just checks if the reference is zero (all bytes are 0). True boolean value is not represented by 0, so null check passes.
Then, it tries to access object located at this "address". It cannot work, you access protected memory and get access violation error. However, as explained in this answer:
but if (A) the access violation happened at an address lower than
0x00010000 and (B) such a violation is found to have happened by code
that was jitted, then it is turned into a NullReferenceException,
otherwise it gets turned into an AccessViolationException
So it is turned into NullReferenceException you observe.
Interesting that if you change your code like this:
MyEvent(null, false);
Then it will run without errors, because false is represented by zero byte, and so e == null check will return true.
You can play with this code a bit more, for example change event type to int:
public static event EventHandler<int> MyEvent;
And then do:
MyEvent(null, 0x00010001);
Now it will throw AccessViolationException instead of NullReferenceException as the linked answer claims (now we are trying to access memory at location higher than 0x00010000 so runtime does not convert this access violation into null reference exception).
Here is another fun thing, we are using code from this answer to obtain memory address of .NET object at runtime, then pass that address into handler:
public static event EventHandler<IntPtr> MyEvent;
public static unsafe void Main() {
// it's not even EventArgs, it's string
var fakeArgument = "Hello world!";
// some black magic to get address
var typedRef = __makeref(fakeArgument);
IntPtr ptr = **(IntPtr**)(&typedRef);
EventInfo eventInfo = typeof(Program).GetEvent(nameof(MyEvent));
AddEventHandler(eventInfo, null, (object s, EventArgs e) => {
if (e == null) return;
// e is actually a string here, not EventArgs...
Console.WriteLine(e?.ToString());
});
MyEvent(null, ptr);
}
This code outputs "Hello world!", for the reasons explained above.
So long story short - don't use such dangerous undocumented internal apis in real code.

Using anonymous event in function

public static void OnAutoScrollToEndChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
{
/* ... */
var scrollToEndHandler = new NotifyCollectionChangedEventHandler((sender, args) => // 수정
{
if (listBox.Items.Count > 0)
{
object lastItem = listBox.Items[listBox.Items.Count - 1];
listBoxItems.MoveCurrentTo(lastItem);
listBox.ScrollIntoView(lastItem);
}
});
if (isAutoScroll)
{
source.CollectionChanged += scrollToEndHandler; // A
}
else
{
source.CollectionChanged -= scrollToEndHandler; //B
}
}
https://michlg.wordpress.com/2010/01/17/listbox-automatically-scroll-to-bottom/
This code is referenced by upper URL.
A(scrollToEndHandler) and B(scrollToEndHandler) are in a function.
When 'AutoScrollToEndProperty' is changed, 'OnAutoScrollToEndChanged' will be called all the time.
I wondering whether These are same reference. Thanks.
If your question is basically, "Does unsubscription actually work here?" the answer is C# compiler implementation-specific, theoretically.
On a practical basis, the body of the lambda expression doesn't capture any local variables, but does capture this (by referring to listBox)... so I'd expect the compiler to generate an instance method containing the body of the code in the lambda expression.
If the method is called multiple times on the same target (i.e. this refers to the same object), I'd expect scrollToEndHandler to be a distinct but equal delegate each time - in other words, it would create a new delegate object in each call (which it probably wouldn't if the lambda expression didn't capture anything, and could be implemented as a static method and the delegate cached)... but the event subscription/unsubscription will still work because the delegates are equal (referring to the same target for the same method).
If the lambda expression referred to any local variables in the method, then event handling wouldn't work, because the compiler would capture those local variables via a separate nested class containing the relevant variables and a method for the delegate code, and each method invocation of OnAutoScrollToEndChanged would create a new instance of that nested class, leading to unequal delegates.
As I say though, that's all implementation-specific... it would be better to just move that code into a separate instance method, to make it clearer that it would work.

Unable to call Delegate ResponseRecieved.Invoke( ) method in c#

I am using delegate to use invoke method with 2 parameters. but it is giving null reference exception error:
Object reference not set to an instance of an object.
Unable to shift control to invoke method in main page. Can anyone tell me why its so..? thanks ..
public override event ResponseRecievedDelegate ResponseRecieved;
if (reqName == REQUEST_NAME.abc)
{
IJsonParser parser = new JsonParser();
Object resp = parser.GetData(responseString );
ResponseRecieved.Invoke(reqName, resp); // unable to invoke this method giving null exception ..
}
invoke method implementation is like:
private void OnResponseReceived(REQUEST_NAME requestName, Object response)
{
if (requestName == REQUEST_NAME.abc)
{
//------------
}
else if (requestName == REQUEST_NAME.def)
{
//------------
}
}
you need to check
if(ResponseRecieved != null)
before calling the event
in general, when using events, before using them we need to check for null. you can read here on the subject
Invoking an event - Once a class has declared an event, it can treat
that event just like a field of the indicated delegate type. The field
will either be null, if no client has hooked up a delegate to the
event, or else it refers to a delegate that should be called when the
event is invoked. Thus, invoking an event is generally done by first
checking for null and then calling the event.
if you want the OnResponseReceived method to be called you need to register it to the event. you can do it like this:
ResponseRecieved += OnResponseReceived;
make sure OnResponseReceived is in the correct format and you do it before you call the event.
another way will be just call the method...

storing/passing delegates as variables

I am fairly new to C# and was working on a a way to implement a dynamic GUI which uses the serial communication. I originally come from C, so the concept of function pointer is familiar.
Basically I want to invoke a answerFunction() function when the serial command has been processed.
In Theory:
I have a Class lbl_txtBox_Pair which is dynamically created on runtime.
I have a Class comObject which communicates with the serial Port.
I have a third class comPacket which holds all information regarding one serial command.
in an Object of Class lbl_txtBox_Pair I instantiate a Packet and tell it which function should be called when the serial command is finished.
I give the packet Object to the comObject Instance.
after being processed the comObject wants to signal the original sender of the packet by calling the delegate which is stored in the Packet Object.
For some reason I can't get it to work. It tells me that the Attribute of Packet is not callable. Am I doing something terribly wrong?
Here is the Code:
first the code in Class "lbl_txtBox_Pair". I create the comPacket here and give it to the comObject.
public delegate void answerHandler( comPacket packet);
public void txb_value_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Return)
{
answerHandler answerMethod = new answerHandler(this.processAnswer);
comPacket question = new comPacket(this.command, answerMethod, 1);
comObject.addPacket(question);
}
}
The constructor of comPacket. Here the delegate gets stored to be called later.
public Delegate answerFunction;
public comPacket(string cmd, Delegate func, int prio)
{
this.cmd = cmd;
answerFunction = func;
this.prio = prio;
}
In the comObject the Packets get processed. When finished I want to call the function stored in the Packet. The comObject runs in a different Thread by the way.
if (this.isEndtocken(inputline))
{
listen = false;
packet.answerFunction(packet);
}
And here it is were it breaks. packet.answerFunction(packet); wont execute and says it can't be called as Method.
Can anybody see where it goes wrong? I think it seems like the delegate looses the information that it is a delegate or something.
Or do I have to completely restructure the code to use other types of callback / Event Methods?
Change your comPacket to take a strongly typed delegate:
public answerHandler answerFunction;
public comPacket(string cmd, answerHandler func, int prio)
{
this.cmd = cmd;
answerFunction = func;
this.prio = prio;
}
If you still want to keep the delegate reference weakly typed, you can leverage DynamicInvoke instead: http://msdn.microsoft.com/en-us/library/system.delegate.dynamicinvoke.aspx
EDIT: Another option if you want to maintain strongly typed delegates yet have different usages is to leverage generics. Your delegate can be housed in a generic class and tie its signature against that generic type.
I can't leave a comment so I have to post this as an answer instead.
Delegates (and events and stuff) can usually only be "invoked" by the object that contains them.
So if you have
class MyClass {
public event Action someEvent;
// you can also replace Action with the name of your delegate type
}
and you try to do
MyClass x = new MyClass();
x.someEvent.Invoke();
Then that's an error. If you want other objects to be able to invoke the event, you'll have do add a method to MyClass like this:
public void InvokeMyEvent() {
someEvent.Invoke();
}
(I forget whether you still have to do this for static events)

How to add a more generic event handler to an event at runtime

If I have a type that inherits from EventArgs (lets call it EventArgs1), and a further bunch of classes that inherit from EventArgs1 (lets call them collectively EventArgsX), and then a bunch of events that are are of the type EventHandler<EventArgsX>, if at runtime I am passed the EventInfo for one of these events and I want to add an event handler that expects a second argument of type EventArgs1 (e.g. MyEventHandler(object sender, EventArgs1 e)) how would I do it?
If the event was of type EventHandler<EventArgs1> then I would just do this:
eventInfo.AddEventHandler(this, new EventHandler<EventArgs1>(MyEventHandler));
But this throws an exception when the event is of type EventHandler<EventArgsX>, and since I don't know what EventArgsX is at compile time I can't simply new up an EventHandler<EventArgsX> If I did know which event I was adding the handler to at compile time then this would be entirely acceptable:
MyEvent += MyEventHandler
But I simply can't work out how to do this at runtime. Any suggestions?
I can't simply new up an EventHandler<EventArgsX>
Sure you can, although you need to do it using Delegate.CreateDelegate() and reflection. Assuming MyEventHandler is an instance method on this, you could do it like this:
var eventInfo = …;
EventHandler<EventArgs1> badHandler = MyEventHandler;
var goodHandler = Delegate.CreateDelegate(
eventInfo.EventHandlerType, this, badHandler.Method);
eventInfo.AddEventHandler(this, goodHandler);
UPDATE
I have read the question backwards, essentially; I will leave this answer in case someone else who is trying to do the reverse finds this question.
Basically, you can't do what you're trying to do. An event can't add a delegate if the delegate's EventArgs type is a subclass of the event's declared type. You're trying to use covariance when the input parameter is contravariant.
Assume these classes
class EventArgs1 : EventArgs {}
class EventArgsA : EventArgs1 {}
class EventArgsB : EventArgs1 {}
Consider what would happen if you could add a method void Handle(object sender, EventArgsA args) to an event with the signature void SomeEvent(object sender, EventArgs1 args). The event source might do this:
if (SomeEvent != null)
{
var args = new EventArgsB();
SomeEvent(this, args); //this line is perfectly legal, as EventArgsB inherits from EventArgs1.
}
However, when the runtime gets to this delegate in the event's invocation list, it now has to pass the args object to Handle(object, EventArgsA), which it of course can't do, since an EventArgsB instance is not an EventArgsA instance. A run-time exception would result. In reality, the mismatch is caught when you add the delegate, so the exception is thrown then.

Categories