Subscribe to an event of a loaded assembly - c#

I am trying to load an assembly during run-time and subscribe to its events. In my scenario the dll file has an ADD method that gets two integers as arguments and raises an event with a custom event argument that contains the sum.
Here is a part of my code to load the Dll file:
Assembly asm = Assembly.LoadFile(#"C:\Projects\Dll1.Dll");
Type typ = asm.GetType("DLL1.Class1", true, true);
var method = typ.GetMethod("add");
var obj = Activator.CreateInstance(typ);
EventInfo ev1 = typ.GetEvents()[0]; // just to check if I have the proper event
Type tDelegate = ev1.EventHandlerType; // just to check if I have the proper delegate
method.Invoke(obj, new object[] { 1, 0 });
But, I have no idea how to subscribe to the event raised by the assembly. Any help would be appreciated.
Added: example DLL source
namespace Dll1
{
public class Class1
{
int c = 0;
public void add(int a, int b)
{
c = a + b;
if (Added !=null)
Added(this, new AddArgs(c));
}
public delegate void AddHandler(object sender, AddArgs e);
public event AddHandler Added;
}
public class AddArgs : EventArgs
{
private int intResult;
public AddArgs(int _Value)
{
intResult = _Value;
}
public int Result
{
get { return intResult; }
}
}
}

Just take the ev1 you already have and call AddEventHandler like this:
ev1.AddEventHandler(obj, MyEventHandlerMethod);
however, you'll want to make sure you cleanup the handler by calling RemoveEventHandler so that garbage collection can occur.
ev1.RemoveEventHandler(obj, MyEventHandlerMethod);

Related

Why is "MyEvent" always make compiler error "CS0070"?

how to fixed CS0070 error?
Error:
Error CS0070 The event 'Demo.MyEvent' can only appear on the left hand side of += or -= (except when used from within the type 'Demo')
Code:
class Demo
{
public event EventHandler<int> MyEvent;
public void Handler(object sender, int arg)
{
Console.WriteLine($"I just go {arg}");
}
}
class Program
{
static void Main(string[] args)
{
var demo = new Demo();
var eventInfo = typeof(Demo).GetEvent("MyEvent");
var handlerMethod = demo.GetType().GetMethod("Handler");
var handler = Delegate.CreateDelegate(
eventInfo.EventHandlerType,
null,
handlerMethod
);
eventInfo.AddEventHandler(demo, handler);
demo.MyEvent?.Invoke(null, 312);
}
}
Error line:
demo.MyEvent?.Invoke(null, 312);
Field-like events (which this is) act like a field to the declaring type, but just appear like an event add/remove pair to external types. This means that only the type that declares the event can do things like access the current value, which is required in order to invoke the backing delegate. Basically, there's a hidden private field that the compiled declares that you can't see - and when you access the event from within the type, you're talking to the field directly. But when accessing the event from outside, you have to go via the accessors - and the only accessors that C# provides are the add and remove accessors.
If you write a method inside Demo, that method will be able to invoke the event.
Event must be invoked directly form it's class, if your scenario requires to invoke it from outside the event then simply encapsulate your event with a method:
public void InvokeMyEvent(int value)
{
MyEvent?.Invoke(this,value);
}
Then subscribe to it easily with a short code:
demo.MyEvent += MyEvent_EventHandeler;
private void My_EventHandeler(object sender, int e)
{
//enter code here
}
Or even shorter with lambda:
demo.MyEvent += (s, e) =>
{
//enter code here
}
Invoke it from anywhere:
demo.InvokeMyEvent(321);
Thanks Mr. Marc Gravell.
Excuse me, My code is wrong.
Correct code is:
namespace ConsoleApp1
{
class Demo
{
public event EventHandler<int> MyEvent;
public void Handler(object sender, int arg)
{
Console.WriteLine($"I just go {arg}");
}
public static void Main(string[] args)
{
var demo = new Demo();
var eventInfo = typeof(Demo).GetEvent("MyEvent");
var handlerMethod = demo.GetType().GetMethod("Handler");
var handler = Delegate.CreateDelegate(
eventInfo.EventHandlerType,
null,
handlerMethod
);
eventInfo.AddEventHandler(demo, handler);
demo.MyEvent?.Invoke(null, 312);
}
}
}

c# Adding a listener to Event with only the method name (text) [duplicate]

This question already has answers here:
AddEventHandler using reflection
(3 answers)
Closed 1 year ago.
I am trying to use Reflection to add a method from a class to an event. Here is my setup.
Have a class with a keyPressDown & keyPressUp method. I also have an Event in this class. keyPressDown & keyPressUp will fire whatever methods are subscribed to it (if any).
These additional methods are controlling RGB light emitting diodes. One method can flash a color, another can fade a color, etc..
I can subscribe to the event like myEventKeyUp += myClass.MethodA;
My problem is, I am storing the configuration the user wants in a database. So the only thing I can think of is storing the Method Name as text and use reflection to add it to the event handler.
Code Example:
Class MyClass
public event delegateKeyDown keyDownEvent;
public event delegateKeyUp keyUpEvent;
public void KeyUp()
{
joystick.SetBtn(false, 1, vJoyButtonID);
if (keyUpEvent != null) keyUpEvent();
}
public void KeyDown()
{
joystick.SetBtn(true, 1, vJoyButtonID);
// IF WE HAVE ANY LISTENERS THEN FIRE THEM
if (keyDownEvent != null) keyDownEvent();
}
public void MethodA()
{
// DO SOMeTHING HERE
}
Main Form
button.keyDownEvent += button.SetRandomColor;
button.keyUpEvent += button.TurnOff;
What I need to do is something like:
button.keyUpEvent += MyClass.GetType().GetMethod("MethodA");
I know you can't do what I am trying to do with Reflection, I read that I can use reflection to get hold of the delegate that contains the event handler, and add it through that way but I am unsure (or unclear about this).
In the vein of Jim W's link, here's a working example:
using System;
using System.Reflection;
internal class Program
{
private static void Main()
{
var foo = new Foo();
var fooType = foo.GetType();
var eventInfo = fooType.GetEvent("Bar");
var methodInfo = fooType.GetMethod("OnBar", BindingFlags.Static | BindingFlags.Public);
eventInfo.AddEventHandler(foo, Delegate.CreateDelegate(eventInfo.EventHandlerType, methodInfo));
foo.RaiseBar();
Console.ReadKey();
}
}
public class Foo
{
public delegate void BarHandler(object sender, BarEventArgs args);
public event BarHandler Bar;
public void RaiseBar()
{
Bar(this, new BarEventArgs());
}
public static void OnBar(object sender, BarEventArgs args)
{
Console.WriteLine(args.Guid);
}
}
public class BarEventArgs : EventArgs
{
public Guid Guid => Guid.NewGuid();
}
Thanks for both of your help. Between the two examples shown I was able to wrap my head around this. I Made a function to handle the event setup as follows:
private void SetupEventHandlers(RGBButton button,string EventName, string MethodName)
{
// THIS SETS UP THE EVENT HANDLER THAT WILL FIRE ANY ADDITION
// ACTIONS THE USER COULD WANT WHEN A BUTTON IS PRESSED OR RELEASED.
var p = button;
var eventInfo = p.GetType().GetEvent(EventName);
var methodInfo = typeof(RGBButton).GetMethod(MethodName);
try
{
// TRY TO SUBSCRIBE TO OUR KEYUP OR KEYDOWN EVENT
Delegate handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, p, methodInfo);
eventInfo.AddEventHandler(p, handler);
}
catch (Exception)
{
// MOST LIKELY COULDN'T FIND THE METHOD WE ARE TRYING TO FIRE
throw new System.InvalidOperationException("Failed to find method: " + MethodName + "', which is registered as an Event Subscriber.");
}
}
This allows me to add to an event in my class (a few different events) with any given name (stored in a database) all from one function. the var P = button is a class which defines my RGB Led arcade buttons. That class contains the events that get triggered when the button is pressed.

Attaching an event handler through reflexion

I have the following code:
namespace ConsoleApplication
{
static void Main(string[] args)
{
Device device = new Device();
device.Command += new EventHandler<DeviceSpecialArgs>(device_Command);
}
public static void device_Command(Object source, DeviceSpecialArgs args)
{
Console.WriteLine("Command: {0}, Reguest: {1}", args.Command, args.Request);
}
}
}
I have to do the exact same thing but the assembly containing types Device and DeviceSpecialArgs need to be loaded at runtime. I know how to load the assembly with reflection but I find the event handling part puzzling:
namespace ConsoleApplication
{
static void Main(string[] args)
{
// Load the assembly
string dllPath = #"C:\Temp\Device.dll"
Assembly asm = Assembly.LoadFrom(dllPath);
// Instanciate Device
Type deviceType = asm.GetType("Device");
object device = Activator.CreateInstance(deviceType);
// How do I subscribe to the Command event?
}
// args would normally be a DeviceSpecialArgs but since that type is
// unknown at compile time, how do I prototype the handler?
public static void device_Command(Object source, ??? args)
{
Console.WriteLine("Command: {0}, Reguest: {1}", args.Command, args.Request);
}
}
How do I subscribe to the event using reflection? Also, how should I prototype the handler itself since the type of "args" is unknown at compile time? FYI, I'm C# 3 and .NET 3.5.
Firsly, look at the MAF.
Alternative way is to add to the first assembly a reference to the second one. Then create a couple of interfaces in the second assembly and make the classes in the first one to implement them:
public interface IDeviceSpecialArgs
{
string Command { get; }
string Request { get; }
}
public interface IDevice
{
event EventHandler<IDeviceSpecialArgs> Command;
}
The first assembly:
public sealed class DeviceSpecialArgs : EventArgs, IDeviceSpecialArgs
{
private readonly string command;
private readonly string request;
public string Command
{
get { return command; }
}
public string Request
{
get { return request; }
}
public DeviceSpecialArgs(string command, string request)
{
this.command = command;
this.request = request;
}
}
public class Device : IDevice
{
public event EventHandler<IDeviceSpecialArgs> Command;
...
}
In the second assembly simply cast newly instantiated objects to the corresponding interfaces:
IDevice device = Activator.CreateInstance(deviceType) as IDevice;
Now you can subscribe to the Command event, because it is declared in the IDevice interface:
device.Command += new EventHandler<IDeviceSpecialArgs>(device_Command);
EDIT: If you have no control over the assembly you are loading, please try the following code. It just creates a handler with the EventArgs type of the second argument and uses a reflection to get its properties:
internal class DeviceEvent
{
private readonly Type deviceType;
private readonly Type deviceSpecialArgsType;
public DeviceEvent()
{
// Load the assembly
const string dllPath = #"C:\Temp\Device.dll";
Assembly asm = Assembly.LoadFrom(dllPath);
// Get types
deviceType = asm.GetType("Device");
deviceSpecialArgsType = asm.GetType("DeviceSpecialArgs");
// Instantiate Device
object device = Activator.CreateInstance(deviceType);
// Subscribe to the Command event
deviceType.GetEvent("Command").AddEventHandler(device, (Delegate.CreateDelegate(typeof(EventHandler), GetType().GetMethod("Device_Command", BindingFlags.NonPublic))));
}
private void Device_Command(object sender, EventArgs e)
{
string command = deviceSpecialArgsType.GetProperty("Command", BindingFlags.Public).GetValue(e, null).ToString();
string request = deviceSpecialArgsType.GetProperty("Request", BindingFlags.Public).GetValue(e, null).ToString();
...
}
}

Avoiding null pointer exception in delegates

I'm using delegates
in my c# windows forms application project.Using that I'm trying to remove items in a list box. I'm getting this null pointer exception and can somebody suggest a way to avoid that?
Delegate
public delegate void OrderEventDelegate (Object sender, OrderEventArgs args);
OrderEventArgs class
public class OrderEventArgs
{
private String message;
public String Message
{
get { return message; }
set { message = value; }
}
private int tableNo;
public int TableNo
{
get { return tableNo; }
set { tableNo = value; }
}
}
Class 1
public partial class Class1 : Form
{
private event OrderEventDelegate readyEvent;
public Class1(HomeForm parent, int tableNo)
{
InitializeComponent();
readyEvent -= new OrderEventDelegate(parent.readyOrder);
}
public void button_click()
{
OrderEventArgs readyOrderArg = new OrderEventArgs();
readyOrderArg.TableNo = 1;
readyOrderArg.Message = "123";
readyEvent(this, readyOrderArg);
}
}
Here readyEvent -= new OrderEventDelegate(parent.readyOrder);readyOrder() is the method which remove items in the list, which is located in the 'Homeform'.
Exception
It is possible to initialize C# events with an empty delegate. This way it can always be called safely without a null pointer check. As shown in this answer: https://stackoverflow.com/a/340618/2404788.
public delegate void OrderEventDelegate (Object sender, OrderEventArgs args) = delegate {};
If there's a possibility of something being null and you can do something about it/don't want to critically fail when it is, then check for it:
if (readyEvent != null) {
readyEvent( ... );
}
But the point here, I suppose, is that you don't want this thing to be null; so you should subscribe a handler to the event. I'm not sure why you're trying to remove a new instance of the delegate handler, but to add one you would use +=.

c# delegates and events

im trying to learn delegates and events in c#, i understand that an event is some sort of a wrapper for a delegate and a delegate is a pointer for functions/methods...
below is my code but when i run it, nothing is being shown... what could be the problems?
public class ClassHandler
{
public delegate void DoProcesses();
public event DoProcesses DoProcessesEvent;
}
public class Class1
{
public void Func1()
{
Console.WriteLine("Class 1 doing function 1");
}
public void Func2()
{
Console.WriteLine("Class 1 doing function 2");
}
}
public class Class2
{
public void Func1()
{
Console.WriteLine("Class 2 doing function 1");
}
public void Func2()
{
Console.WriteLine("Class 2 doing function 2");
}
}
class Program
{
static void Main(string[] args)
{
Class1 cs1 = new Class1();
Class2 cs2 = new Class2();
ClassHandler main = new ClassHandler();
main.DoProcessesEvent += new ClassHandler.DoProcesses(cs1.Func1);
main.DoProcessesEvent += new ClassHandler.DoProcesses(cs1.Func2);
main.DoProcessesEvent += new ClassHandler.DoProcesses(cs2.Func1);
main.DoProcessesEvent += new ClassHandler.DoProcesses(cs2.Func2);
main.DoProcessesEvent += new ClassHandler.DoProcesses(ff); // this line here is causing an error: An object reference is required for the non-static field, method, or property 'TryDelegatesAndEvents.Program.ff()'
Console.Read();
}
public void ff()
{
Console.WriteLine("gggg");
}
}
UPDATE: how do i raise the event so it will execute the methods already?
Problem with this line: main.DoProcessesEvent += new ClassHandler.DoProcesses(ff)
That is because your method ff() is a non-static method and you can't access it directly like that from a static method.
Make your method ff as static, or create and object of the containing class and assign the method with an instance of it.
For Comments: The reason you are not seeing anything is because you are just binding them to an event DoProcessesEvent, but you are not raising the event any where. You are only defining the handler for the event.
EDIT:
Change your ClassHandler class to:
public class ClassHandler
{
public delegate void DoProcesses();
public event DoProcesses DoProcessesEvent;
public void OnDoProcessEvent()
{
if (DoProcessesEvent != null)
DoProcessesEvent();
}
}
In your Main method before Console.Read(); Type:
main.OnDoProcessEvent();
This will raise the event and it will handled from the application and will give you the following output.
Class 1 doing function 1
Class 1 doing function 2
Class 2 doing function 1
Class 2 doing function 2
gggg
change main.DoProcessesEvent += new ClassHandler.DoProcesses(ff); to main.DoProcessesEvent += new ClassHandler.DoProcesses(new Program().ff); or make ff static
Well it does not compile due to the line:
main.DoProcessesEvent += new ClassHandler.DoProcesses(ff);
The error VS spits out is that:
An object reference is required for the non-static field, method, or property 'ConsoleApplication2.Program.ff()'
Just change your ff() method to be static to get around it.
Eg:
public static void ff()
{
Console.WriteLine("gggg");
}
Besides the problem pointed out in earlier comments, You have to trigger the event.
make a copy of an event before you check it for null and fire it. This will eliminate a potential problem with threading where the event becomes null at the location right between where you check for null and where you fire the event:
// Copy the event delegate before checking/calling
EventHandler copy = DoProcessesEvent ;
if (copy != null)
copy(this, EventArgs.Empty); // Call any handlers on the copied list
This will ensure that your event fires and you will get the result.
Just to add to #Habib's answer, it would be fairly unusual to subscribe instance class methods as event handlers of an object potentially in another scope (e.g. what happens if Class1 goes out of scope, yet main() still has a subscription?). A more common scenario would be to subscribe (and de-subscribe) handlers in the same scope, often in an asynchronous manner (the below events are still raised synchronously).
namespace ConsoleApplication1
{
public delegate void ProcessCompletedEvent(string description);
public class Class1
{
public void Func1()
{
// Do Func1 work
Thread.Sleep(500);
RaiseEvent("Func1 completed");
}
public void Func2()
{
// Do Func2 work
Thread.Sleep(1000);
RaiseEvent("Func2 completed");
}
private void RaiseEvent(string description)
{
if (ProcessCompleted != null)
{
ProcessCompleted(description);
}
}
public event ProcessCompletedEvent ProcessCompleted;
}
class Program
{
static void Main(string[] args)
{
Class1 cs1 = new Class1();
// Wire up event handler
cs1.ProcessCompleted += new ProcessCompletedEvent(MyHandler);
cs1.Func1();
cs1.Func2();
Console.Read();
// Remove the subscription
cs1.ProcessCompleted -= MyHandler;
}
// *** Is in the same scope as main, which subscribes / desubscribes
public static void MyHandler(string description)
{
Console.WriteLine(description);
}
}
}

Categories

Resources