Cannot bind to the target method because ... from Delegate.Create(..) + Reflection - c#

public class AwaitableRoutedEvent
{
private TaskCompletionSource<object> _tcs;
public async Task When<TypeOfControl>(TypeOfControl source, string nameOfEvent)
{
_tcs = new TaskCompletionSource<object>();
var targetEventInfo = source.GetType().GetEvent(nameOfEvent);
Delegate tempEventHandler =
Delegate.CreateDelegate(
targetEventInfo.EventHandlerType,
GetType().GetMethod(nameof(RoutedEventHandler), BindingFlags.Instance | BindingFlags.Public));
try
{
targetEventInfo.AddEventHandler(source, tempEventHandler);
await _tcs.Task;
}
finally
{
targetEventInfo.RemoveEventHandler(source, tempEventHandler);
_tcs = null;
}
}
public void RoutedEventHandler(object sender, RoutedEventArgs arguments)
{
_tcs.TrySetResult(null);
}
}
The calss above represents an 'awaitable' RoutedEventHandler event. The goal is to use it like await new AwaitableRoutedEvent().When(button_object, "click");.
IF I MAKE public void RoutedEventHandler(object sender, RoutedEventArgs arguments) STATIC... and change BindingsFlags... it works. But I don't like static. Non-static version throws an exception: Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.
Did I miss something?

Because RoutedEventHandler is an instance method, when creating a delegate you should supply an instance of the object on which the method is invoked:
Delegate tempEventHandler =
Delegate.CreateDelegate(
targetEventInfo.EventHandlerType,
this,
GetType().GetMethod(nameof(RoutedEventHandler), BindingFlags.Instance | BindingFlags.Public));
And seemingly you do not need that at all because you may write
RoutedEventHandler tempEventHandler = this.MyRoutedEventhander;
public void MyRoutedEventHandler(object sender, RoutedEventArgs arguments)
{
_tcs.TrySetResult(null);
}

Related

C# - Why is there an error using EventHandler<T>?

Doing a Pluralsight video and can't find out why it's wrong.
Error Message on this point:
worker.WorkPerformed += new EventHandler<WorkPerformedEventArgs>(worker_WorkPerformed);
Error Message:
Cannot implicity convert to type
'System.EventHandler<AB_Events.WorkPerformedEventArgs>' to
'AB_Events.WorkPerformedHandler'
Snippets
public delegate int WorkPerformedHandler(object sender,WorkPerformedEventArgs e);
public class Worker
{
public event WorkPerformedHandler WorkPerformed;
protected virtual void OnWorkPerformed(int hours, WorkType workType)
{
var del = WorkPerformed as WorkPerformedHandler;
if (del != null)
{
del(this, new WorkPerformedEventArgs(hours, workType));
}
}
}
And
class Program
{
static void Main(string[] args)
{
var worker = new Worker();
worker.WorkPerformed += new EventHandler<WorkPerformedEventArgs>(worker_WorkPerformed);
Console.ReadKey();
}
public static void worker_WorkPerformed(object sender, WorkPerformedEventArgs e)
{
throw new NotImplementedException();
}
}
Although methods and anonymous functions are implicitly convertible to a delegate type that matches their signature, a delegate instance is not implicitly convertible to another delegate type.
You need to use either EventHandler<TEventArgs> or your WorkPerformedHandler, but not both:
public class Worker
{
public event WorkPerformedHandler WorkPerformed;
//...
}
Or:
public class Worker
{
public event EventHandler<WorkPerformedEventArgs> WorkPerformed;
//...
}
Also, because worker_WorkPerformed can be implicitly converted to either type, the most concise syntax would be this:
worker.WorkPerformed += worker_WorkPerformed; // Implicit conversion
If you do go with your WorkPerformedHandler delegate, make sure you change the return type to void as per the signature of worker_WorkPerformed:
public delegate void WorkPerformedHandler(object sender, WorkPerformedEventArgs e);
It is generally not advisable to return from an event handler anyway, as there can be multiple subscribers.
what i have done is to replace WorkPerformedHandler by EventHandler<WorkPerformedEventArgs>
public delegate int WorkPerformedHandler(object sender, WorkPerformedEventArgs e);
public class Worker
{
public event EventHandler<WorkPerformedEventArgs> WorkPerformed;
protected virtual void OnWorkPerformed(WorkPerformedEventArgs e)
{
WorkPerformed?.Invoke(this, e);
}
}
event subscription :
var worker = new Worker();
worker.WorkPerformed += new EventHandler<WorkPerformedEventArgs>(worker_WorkPerformed);

C# Relection addeventhandler : create Delegate fail cannot bind to the target method

I have tried to add event from external dll file and i got argumentexception "Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type".
I tried to search the solution but they do not work with my code.
This is code in dll file.
public class StartProcess
{
public StartProcess(){}
public delegate void MSTStepInformEventHandler(object sender, MSTStepInformEventArg e);
public event MSTStepInformEventHandler StepInformed;
protected virtual void OnStepInformed(MSTStepInformEventArg e)
{
if (this.StepInformed != null)
{
this.StepInformed(this, e);
}
}
public void Start()
{
this.OnStepInformed(new MSTStepInformEventArg() { Info = DateTime.Now.toString()});
}
public class MSTStepInformEventArg : EventArgs
{
public MSTStepInformEventArg() { }
public string Info { get; set; }
}
And the below is reflection code.
class Test
{
void HandleEvent(object sender, MainDLL.MSTStepInformEventArg e)
{
Console.WriteLine("HandleEvent called " + e.Info);
}
static void Main()
{
Assembly ass = Assembly.LoadFrom("StartProcess.dll");
Type classEx = ass.GetType("StartProcess.StartProcess");
Test test = new Test();
object obj = Activator.CreateInstance(classEx);
MethodInfo method = typeof(Test).GetMethod("HandleEvent", BindingFlags.NonPublic | BindingFlags.Instance);
EventInfo eventInfo = classEx.GetEvent("StepInformed");
Type type = eventInfo.EventHandlerType;
Delegate handler = Delegate.CreateDelegate(type, test, method); // Exception throw is here -----
eventInfo.AddEventHandler(obj, handler);
classEx.InvokeMember("Start", BindingFlags.InvokeMethod, null, obj, null);
}
}
Please help...

C# how to trigger a callback?

This is part of my code snippet.
I want to pass a callback function into test().
therefore, after calling the "del" delegate, the callback() can be trigger automatically?
Functions:
butOK_Click() //when click the button, disable it
test() //a wrapper function calling updateUI function
updateUI() // a long process function
callback() // a callback function enabling the button back
How can i do this?
Thanks
public delegate void updateUIDelegate(bool refresh);
public delegate void asyncCallback();
//...
void butOK_Click(object sender, EventArgs e) {
butOK.Enabled = false;
test();
}
public void updateUI() {
// long function....doing 10s
}
public void callback() {
butOK.Enabled = true;
}
public void test() {
updateUIDelegate del = new updateUIDelegate(updateUI);
del.BeginInvoke(null,null);
//??????????
}
Plesse, try the following:
void button1_Click(object sender, EventArgs e) {
button1.Enabled = false;
BeginAsyncOperation(updateUI);
}
void BeginAsyncOperation(Action operation) {
operation.BeginInvoke(OnAsyncCallback, null);
}
void OnAsyncCallback(IAsyncResult result) {
if(result.IsCompleted) {
if(!InvokeRequired)
callback();
else BeginInvoke(new Action(callback));
}
}
//
public void callback() {
button1.Enabled = true;
// something else
}
public void updateUI() {
// long function....doing 10s
System.Threading.Thread.Sleep(10000);
}
Please also take a look at the following article: Calling Synchronous Methods Asynchronously
You can pass a delegate to a function as a parameter. So add a parameter to your method 'Test' of type 'asyncCallback'. Then in the 'test' method you can just call the delegate method passed in.
Here is some sample code:
class MyClass {
public delegate void updateUIDelegate(bool refresh);
public delegate void asyncCallback();
private void butOK_Click(object sender, EventArgs e)
{
butOK.Enabled = false;
test(new asyncCallback(callback));
}
public void updateUI(bool refresh)
{
// long function....doing 10s
}
public void callback()
{
butOK.Enabled = true;
}
public void test(asyncCallback callbackMethod)
{
updateUIDelegate del = new updateUIDelegate(updateUI);
del.BeginInvoke(true, null, null);
if(callbackMethod != null) callback();
}
}
Not sure whether I understand correctly, but I think you want to re-enable the butOK button after updating the UI. If so, there are two solutions.
1) You may modify
updateUIDelegate del = new updateUIDelegate(updateUI);
into
var del = new Action(() => { updateUI(); callback(); });
I change updateUIDelegate into var here because the definition of updateUI actually doesn't match updateUIDelegate.
2) Refactor callback() to match the definition of AsyncCallback, and pass it as the parameter of BeginInvoke(). That is,
BeginInvoke(callback, null);
This is more elegant or official usage of BeginInvoke, but may require more efforts to refactor the code.

How to attach event handler to an event using reflection?

I know about EventInfo.AddEventHandler(...) method which can be used to attach handler to an event. But what should be done if i can not even define proper signature of the event handler, as in, i don't even have reference to the event args expected by the handler?
I will explain the problem with the proper code.
// Scenario when I have everything available in my solution, Zero Reflection Scenario.
internal class SendCommentsManager
{
public void Customize(IRFQWindowManager rfqWindowManager)
{
rfqWindowManager.SendComment += HandleRfqSendComment;
}
private void HandleRfqSendComment(object sender, SendCommentEventArgs args)
{
args.Cancel = true;
}
}
Now, I want to achieve the same objective by using reflection. I have been able to figure out most of it but when i attach a delegate to the event (using AddEventHandler) it throws "Error binding to target method." exception.
I understand the reason behind this exception, attaching a wrong delegate to an event. But there must be some way to achieve this.
internal class SendCommentsManagerUsingReflection
{
public void Customize(IRFQWindowManager rfqWindowManager)
{
EventInfo eventInfo = rfqWindowManager.GetType().GetEvent("SendComment");
eventInfo.AddEventHandler(rfqWindowManager,
Delegate.CreateDelegate(eventInfo.EventHandlerType, this, "HandleRfqSendComment"));
//<<<<<<<<<<ABOVE LINE IS WHERE I AM GOING WRONG>>>>>>>>>>>>>>
}
private void HandleRfqSendComment(object sender, object args)
{
Type sendCommentArgsType = args.GetType();
PropertyInfo cancelProperty = sendCommentArgsType.GetProperty("Cancel");
cancelProperty.SetValue(args, true, null);
}
}
I think your code is failing because the HandleRfqSendComment is private. Instead you could directly create a delegate to that method, without passing its name to CreateDelegate. You would then need to convert the delegate to the required type, using the following method :
public static Delegate ConvertDelegate(Delegate originalDelegate, Type targetDelegateType)
{
return Delegate.CreateDelegate(
targetDelegateType,
originalDelegate.Target,
originalDelegate.Method);
}
In your code, you could use this method as follows :
EventInfo eventInfo = rfqWindowManager.GetType().GetEvent("SendComment");
Action<object, object> handler = HandleRfqSendComment;
Delegate convertedHandler = ConvertDelegate(handler, eventInfo.EventHandlerType);
eventInfo.AddEventHandler(rfqWindowManager, convertedHandler);
A small addition to the already awesome answers here. Here's a helper class you could use to subscribe to events with actions.
public static partial class ReactiveExtensions
{
public static EventHandler<TEvent> CreateGenericHandler<TEvent>(object target, MethodInfo method)
{
return (EventHandler<TEvent>)Delegate
.CreateDelegate(typeof(EventHandler<TEvent>),
target, method);
}
public static EventHandler CreateHandler(object target, MethodInfo method)
{
return (EventHandler)Delegate
.CreateDelegate(typeof(EventHandler),
target, method);
}
static void BindEventToAction(object target, EventInfo eventInfo, Delegate action)
{
MethodInfo method;
if (eventInfo.EventHandlerType.IsGenericType)
{
method = typeof(ReactiveExtensions)
.GetMethod(nameof(CreateGenericHandler))
.MakeGenericMethod(
eventInfo.EventHandlerType.GetGenericArguments());
}
else
{
method = typeof(ReactiveExtensions)
.GetMethod(nameof(CreateHandler));
}
Delegate #delegate = (Delegate)method.Invoke(null,
new object[] { action.Target, action.Method });
eventInfo.AddEventHandler(target, #delegate);
}
}
Here's a sample on how to use this:
public static partial class ReactiveExtensions
{
public static void Subscribe<T>(T source, string eventName)
{
EventInfo eventInfo = typeof(T).GetEvent(eventName);
Action<object, object> action = (s, e) =>
{
Console.WriteLine("Event Called");
};
BindEventToAction(source, eventInfo, action);
}
}

AddEventHandler using reflection

I have this piece of code that does not work:
public CartaoCidadao()
{
InitializeComponent();
object o = WebDAV.Classes.SCWatcher.LoadAssembly();
MethodInfo method =
this.GetType().GetMethod("Inserted",
BindingFlags.NonPublic | BindingFlags.Instance);
EventInfo eventInfo = o.GetType().GetEvent("CardInserted");
Type type = eventInfo.EventHandlerType;
Delegate handler = Delegate.CreateDelegate(type, this , method);
eventInfo.AddEventHandler(o, handler);
}
void Inserted(string readerName, string cardName)
{
System.Windows.Forms.MessageBox.Show(readerName);
}
The Event CardInserted exists in another DLL file and object "o" loads OK. The delegate handler has a value after effect. I only can't fire the event.
Here's a sample showing how to attach an event using reflection:
class Program
{
static void Main(string[] args)
{
var p = new Program();
var eventInfo = p.GetType().GetEvent("TestEvent");
var methodInfo = p.GetType().GetMethod("TestMethod");
Delegate handler =
Delegate.CreateDelegate(eventInfo.EventHandlerType,
p,
methodInfo);
eventInfo.AddEventHandler(p, handler);
p.Test();
}
public event Func<string> TestEvent;
public string TestMethod()
{
return "Hello World";
}
public void Test()
{
if (TestEvent != null)
{
Console.WriteLine(TestEvent());
}
}
}
Here's a short but complete example which does work:
using System;
using System.Reflection;
class EventPublisher
{
public event EventHandler TestEvent;
public void RaiseEvent()
{
TestEvent(this, EventArgs.Empty);
}
}
class Test
{
void HandleEvent(object sender, EventArgs e)
{
Console.WriteLine("HandleEvent called");
}
static void Main()
{
// Find the handler method
Test test = new Test();
EventPublisher publisher = new EventPublisher();
MethodInfo method = typeof(Test).GetMethod
("HandleEvent", BindingFlags.NonPublic | BindingFlags.Instance);
// Subscribe to the event
EventInfo eventInfo = typeof(EventPublisher).GetEvent("TestEvent");
Type type = eventInfo.EventHandlerType;
Delegate handler = Delegate.CreateDelegate(type, test, method);
// Raise the event
eventInfo.AddEventHandler(publisher, handler);
publisher.RaiseEvent();
}
}
Now, when you say "I only can't fire event", what exactly do you mean? You're not meant to be able to raise events yourself - it's up to the event publisher to do that. Does all of the code you've actually presented to us work? If so, it seems that it's not adding the event handler that's the problem.
Could you give more information?
When you say it doesn't work... what happens? Nothing? An exception?
Thoughts:
are both the event and the handler public? If not, you'll need to pass suitable BindingFlags to the GetEvent / GetMethod calls.
does the signature of the handler match?
Here's a working example (note I'm using a static handler, hence the null in Delegate.CreateDelegate):
using System;
using System.Reflection;
class Test
{
public event EventHandler SomeEvent;
public void OnSomeEvent()
{
if (SomeEvent != null) SomeEvent(this, EventArgs.Empty);
}
static void Main()
{
Test obj = new Test();
EventInfo evt = obj.GetType().GetEvent("SomeEvent");
MethodInfo handler = typeof(Test)
.GetMethod("MyHandler");
Delegate del = Delegate.CreateDelegate(
evt.EventHandlerType, null, handler);
evt.AddEventHandler(obj, del);
obj.OnSomeEvent();
}
public static void MyHandler(object sender, EventArgs args)
{
Console.WriteLine("hi");
}
}

Categories