I am adding a custom event to a DataGridView to handle cell clicks. This
needs a parameter passed to it.
I first attempt to delete the previous event handler, as I am sending the same DataGridView in and populating it each time.
//To delete the old handle
DataGridViewToPopulate.CellClick -= (sender, e) => DataGridView_CellClick(sender, e, SourceObject);
//To add new handle
DataGridViewToPopulate.CellClick += (sender, e) => DataGridView_CellClick(sender, e, SourceObject);
My problem is, when this runs the second time, and the SourceObject has changed, the event still has the original SourceObject being sent to it (I believe the original handle is never removed).
I need to dynamically remove all CellClick events (or even all, there aren't that many).
Maybe "this", when you are calling the second time the -= operator, is a different object than the first time you executed the +=. So that -= will not detach that first object, and you would be attaching a second object while not removing the first one.
If that's the case, you should try to find the way to detach that first object.
I was able to find a solution with reflection,
private static void UnsubscribeOne(object target)
{
Delegate[] subscribers;
Type targetType;
string EventName = "EVENT_DATAGRIDVIEWCELLCLICK";
targetType = target.GetType();
do
{
FieldInfo[] fields = targetType.GetFields(BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic);
foreach (FieldInfo field in fields)
{
if (field.Name == EventName)
{
EventHandlerList eventHandlers = ((EventHandlerList)(target.GetType().GetProperty("Events", (BindingFlags.FlattenHierarchy | (BindingFlags.NonPublic | BindingFlags.Instance))).GetValue(target, null)));
Delegate d = eventHandlers[field.GetValue(target)];
if ((!(d == null)))
{
subscribers = d.GetInvocationList();
foreach (Delegate d1 in subscribers)
{
targetType.GetEvent("CellClick").RemoveEventHandler(target, d1);
}
return;
}
}
}
targetType = targetType.BaseType;
} while (targetType != null);
}
Related
I see that the ThreadException is a public static event on the System.Windows.Forms.Application class.
Normally if I were using reflection to remove an event handler (e.g. clearing an anonymous handler) from some object, I do something like this (stripped down a bit):
EventInfo ei = obj.GetType().GetEvent("FooEvent");
FieldInfo fi = obj.GetType().GetField("FooEvent", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
Delegate del = (Delegate)fi.GetValue(obj)
foreach(Delegate d in del.GetInvocationList())
{
ei.RemoveEventHandler(obj, d);
}
But this approach doesn't seem to work for the System.Windows.Forms.Application.ThreadException public static event.
There's a field called "eventHandlers" on the Application type, but it doesn't seem to have any value when I call GetValue(null) from that FieldInfo object, and I know for certain that there's a handler attached because I added one immediately before calling the test:
System.Windows.Forms.Application.ThreadException += ...my test handler...;
Type t = typeof(System.Windows.Forms.Application);
EventInfo ei = t.GetEvent("ThreadException");
FieldInfo fi = t.GetField("eventHandlers", BindingFlags.NonPublic | BindingFlags.Static);
object test = fi.GetValue(null); // Results in null
...so I assume "eventHandlers" is simply the wrong field to use, but I don't see any other ones that look like good candidates. Any ideas about how I can go about doing this?
How to do what you're asking
Events are designed to wrap the delegate and conceal the invocation list from the caller. So there is a reason this is difficult.
This code is a workaround. I don't recommend it-- it could easily break with a newer version of the CLR. But it seems to work right now, if this is important to you.
First, take a look at the source code for Application. You'll find that the thread exceptions event has a custom event accessor that stores the event handlers in a different, nested, private class called ThreadContext. Here is a snip:
//From .NET reference source
public static event ThreadExceptionEventHandler ThreadException
{
add
{
Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "AffectThreadBehavior Demanded");
IntSecurity.AffectThreadBehavior.Demand();
ThreadContext current = ThreadContext.FromCurrent();
lock(current)
{
current.threadExceptionHandler = value; //Here is where it gets stored
}
}
To do what you're asking, we need to get ahold of that instance, which we can do with this tricky code:
static private Type ApplicationType => typeof(Application);
static private Type ThreadContextType => ApplicationType.GetNestedType("ThreadContext", BindingFlags.NonPublic);
static private object GetCurrentThreadContext()
{
var threadContextCurrentMethod = ThreadContextType.GetMethod("FromCurrent", BindingFlags.Static | BindingFlags.NonPublic);
var threadContext = threadContextCurrentMethod.Invoke(null, new object[] { });
return threadContext;
}
Now if you look at the code again, you may notice that the handler is actually assigned, not added. In other word: there can only be one handler. That's right!!! Even though you set it using +=, it is stored in the ThreadContext using =... so it will replace any previous handler.
So if you want to see what method is being referenced, you can retrieve it with
static private ThreadExceptionEventHandler GetCurrentThreadExceptionEventHandler()
{
var threadContext = GetCurrentThreadContext();
var threadExceptionHandler = ThreadContextType.GetField("threadExceptionHandler", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(threadContext) as System.Threading.ThreadExceptionEventHandler;
return threadExceptionHandler;
}
And then we can remove it with
Application.ThreadException -= GetCurrentThreadExceptionEventHandler();
A complete solution looks like this:
static private Type ApplicationType => typeof(Application);
static private Type ThreadContextType => ApplicationType.GetNestedType("ThreadContext", BindingFlags.NonPublic);
static private object GetCurrentThreadContext()
{
var threadContextCurrentMethod = ThreadContextType.GetMethod("FromCurrent", BindingFlags.Static | BindingFlags.NonPublic);
var threadContext = threadContextCurrentMethod.Invoke(null, new object[] { });
return threadContext;
}
static private void RemoveThreadExceptionEventHandler()
{
var threadContext = GetCurrentThreadContext();
var threadExceptionHandler = ThreadContextType.GetField("threadExceptionHandler", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(threadContext) as System.Threading.ThreadExceptionEventHandler;
Application.ThreadException -= threadExceptionHandler;
}
Something else that may work just as well
If you just don't want any handler to fire, you might be fine just replacing it (since there is only one) with an empty method:
Application.ThreadException += (s,e) => {};
However this will suppress the default handler behavior, which is to show a dialog.
We have an application that is well beyond a quick design improvement and is in a state where everything works, but there is a memory leak. We cannot use any of the popular profilers out there because we're sandboxed inside the Unity3D engine in some old version of Mono.
We have many transient objects with many events at play, and we suspect some poor design and missing event unsubscriptions could be at play here.
I'd love to know if anyone can think of some simple ways to scan the project for missing event unsubscribes. I'm hoping it is possible to use Visual Studio itself to write a script that can look at an event and tell us if the -= and += are in equal existence in the project.
Counting -= and += in code is very error-prone, because event-related memory leaks are most often caused by some conditional execution.
If you're short of profilers, you could use Reflection to enumerate any object's field-like event subscribers at any given time using something like this:
public static class InvocationHelper
{
public static Dictionary<string,Delegate[]> GetInvocationList<TClass>(TClass source)
{
if (null == source)
return new Dictionary<string, Delegate[]>();
var retval = new Dictionary<string,Delegate[]>();
var members = typeof (TClass).GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).
Where(t=>t.MemberType == MemberTypes.Event).ToArray();
foreach (var memberInfo in members)
{
var field = typeof (TClass).GetField(memberInfo.Name,
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field == null)
continue;
var delegateField = field.GetValue(source) as Delegate;
if(null == delegateField)
continue;
retval[memberInfo.Name] = delegateField.GetInvocationList();
}
return retval;
}
}
Method above walks through all events in source and returns dictionary with invocation lists for events encountered.
This works only for field-like events (and for custom events you can count subscribers manually).
For example, suppose you have some class with field-like event SomeEvent:
public class SomeEventHolder
{
public delegate void SomeEventHandler(bool par);
public event SomeEventHandler SomeEvent;
protected virtual void OnSomeEvent(bool par)
{
var handler = SomeEvent;
if (handler != null) handler(par);
}
}
You create instance of this class and subscribe three times to SomeEvent but unsubscribe only once:
var instance = new SomeEventHolder();
//....
instance.SomeEvent += inst_SomeEvent;
instance.SomeEvent += inst_SomeOther;
instance.SomeEvent += inst_SomeEvent;
instance.SomeEvent -= inst_SomeEvent;
Then, you can call GetInvocationList for instance and get subscribers left, and, say, output their list to console:
var invocationList = InvocationHelper.GetInvocationList(instance);
foreach (var item in invocationList)
{
Console.WriteLine("Subscribers for {0} : {1}",
item.Key,
string.Join(Environment.NewLine, item.Value.Select(s => s.Method.ToString())));
}
This would result in
Subscribers for SomeEvent : Void inst_SomeEvent(Boolean)
Void inst_SomeOther(Boolean)
I'm writing an application in winForm. I have a panel in from1 and it has many event handler.
When I dispose panel1 and create new panel , the previous events exist & they fire. For removing panel1 events I have tryed code below.
panel1.Click -= clickHandle_1 ;
But it doesn't work every where in program code. for example in another method.
How can I remove all previous events of panel1 when I create new panel1 ?
According to this,
for cancelling all Click Events of panel1 do this:
FieldInfo f1 = typeof(Control).GetField("EventClick", BindingFlags.Static| BindingFlags.NonPublic);
object obj = f1.GetValue(panel1);
PropertyInfo pi = panel1.GetType().GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance);
EventHandlerList list = (EventHandlerList)pi.GetValue(panel1, null);
list.RemoveHandler(obj, list[obj]);
And for cancelling other events of panel1 change EventClick to event name that you want to remove.
One can get all event names using this code:
EventInfo[] info = type.GetEvents();
for (int i = 0; i < info.Length; i++)
{
Console.WriteLine(info[i].Name + "\n");
}
Normally when you Dispose an object/control, all the members of that object/control including Event handler list should be disposed too and unusable. So when you say your panel1 is disposed, it's impossible to fire any event on that disposed control (such as Resize). You should check if you surely dispose your panel. Here is a way you can use to remove all the Event handlers of a control using Reflection to Dispose the Events (type of EventHandlerList):
public void RemoveHandlerList(Control c){
EventHandlerList list = (EventHandlerList) typeof(Control).GetProperty("Events", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(c, null);
typeof(EventHandlerList).GetMethod("Dispose").Invoke(list, null);
}
Remove them one at a time:
panel1.Click -= clickHandle_1;
There is no clean way of unsubscribing all event handlers at once. Keep track of what you're subscribing and unsubscribe them accordingly.
Not sure if this will work but this is closest I came with:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private Dictionary<string, EventHandler> _handlers = new Dictionary<string, EventHandler>();
TextBox _txt = new TextBox();
void WireHandlers()
{
// get references of your handlers
EventHandler _hnd1 = delegate { return; }; // here will be your named method. This is only for example
EventHandler _hnd2 = delegate { return; }; // here will be your named method. This is only for example
// wire handlers
_txt.Click += _hnd1;
_txt.TextChanged += _hnd2;
// save wired handler collection
_handlers.Add("Click", _hnd1);
_handlers.Add("TextChanged", _hnd2);
}
void UnwireHandlers()
{
// lets unwire all handlers
foreach (var kvp in _handlers)
{
// inspect keyValuePair - each key corresponds to the name of some event
switch (kvp.Key)
{
case "Click":
_txt.Click -= kvp.Value;
break;
case "TextChanged":
_txt.TextChanged -= kvp.Value;
break;
default:
throw new Exception("no such handler found");
}
}
_handlers.Clear();
}
}
My constructor parses its instance for events with a certain attribute:
_EventAttributes = from EventDescriptor a in TypeDescriptor.GetEvents(this)
let attribute = a.Attributes[typeof(MyAttribute)] as MyAttribute
where attribute != null
select new EventAttributeTuple { Event = a, Attribute = attribute };
Later in the code i want to check whether the corresponding event is null (nobody is interested in the event) to decide whether or not i have to write it in my XML output:
//write events
foreach (var attr in _EventAttributes)
{
if (check corresponding attr.Event object is null)
{
writer.WriteAttributeString(attr.Attribute.Name, GetRPCAdress(attr.Event.Name));
}
}
EDIT:
Getting the EventInfo is also quite easy:
var evtInfo = this.GetType().GetEvent(attr.Event.Name);
But still, i don't know how to check whether the event has subscribers or not.
EDIT2:
After looking at the generated code with dotPeek, I think there is no chance to access any field here:
//original code
[MyAttribute("onclick")]
public event EventHandler<MouseArg> Click;
//generated code
[MyAttribute("onclick")]
public event EventHandler<MouseArg> Click
{
add
{
EventHandler<MouseArg> eventHandler = this.Click;
EventHandler<MouseArg> comparand;
do
{
comparand = eventHandler;
eventHandler = Interlocked.CompareExchange<EventHandler<MouseArg>>(ref this.Click, comparand + value, comparand);
}
while (eventHandler != comparand);
}
remove
{
EventHandler<MouseArg> eventHandler = this.Click;
EventHandler<MouseArg> comparand;
do
{
comparand = eventHandler;
eventHandler = Interlocked.CompareExchange<EventHandler<MouseArg>>(ref this.Click, comparand - value, comparand);
}
while (eventHandler != comparand);
}
}
There may be a chance, if i handle all of the event add/remove operations myself, but that seems very tedious. Anyone has a better idea?
EDIT3:
I got it, i was using this.GetType() which does not give me the declaring type but was a subclass of the class which declares the event, hence i was not able to retrieve the field.
It now works like this:
var evtValue = attr.Event.ComponentType.GetField(attr.Event.Name,
BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this);
Sorry, this is impossible in general case, for instance:
class CounterExample {
public event EventHandler MyEvent {
add {
// You can't control me here: whether I truly add an event or not
// and even if I always add the event - where I store it
}
remove {
// You can't control me here: whether I truly remove an event or not
// and even if I always remove the event - from where I delete it
}
}
}
But in many cases you can do a hack (be careful though):
class SimpleCase {
// Usual event realization (default add and remove accessors)
public event EventHandler MyEvent;
}
...
SimpleCase cs = new SimpleCase();
cs.MyEvent += ...
...
// Since MyEvent has default accessors, lets check the default storage:
FieldInfo fi = cs.GetType().GetField("MyEvent", BindingFlags.NonPublic | BindingFlags.Instance);
Boolean myEventAssigned = !Object.ReferenceEquals(fi.GetValue(cs), null);
I have 2 handlers using the same form. How do I remove the handlers before adding the new one (C#)?
If you are working in the form itself, you should be able to do something like:
PseudoCode:
Delegate[] events = Form1.SomeEvent.GetInvokationList();
foreach (Delegate d in events)
{
Form1.SomeEvent -= d;
}
From outside of the form, your SOL.
If you know what those handlers are, just remove them in the same way that you subscribed to them, except with -= instead of +=.
If you don't know what the handlers are, you can't remove them - the idea being that the event encapsulation prevents one interested party from clobbering the interests of another class in observing an event.
EDIT: I've been assuming that you're talking about an event implemented by a different class, e.g. a control. If your class "owns" the event, then just set the relevant variable to null.
I realize this question is rather old, but hopefully it will help someone out. You can unregister all the event handlers for any class with a little reflection.
public static void UnregisterAllEvents(object objectWithEvents)
{
Type theType = objectWithEvents.GetType();
//Even though the events are public, the FieldInfo associated with them is private
foreach (System.Reflection.FieldInfo field in theType.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance))
{
//eventInfo will be null if this is a normal field and not an event.
System.Reflection.EventInfo eventInfo = theType.GetEvent(field.Name);
if (eventInfo != null)
{
MulticastDelegate multicastDelegate = field.GetValue(objectWithEvents) as MulticastDelegate;
if (multicastDelegate != null)
{
foreach (Delegate _delegate in multicastDelegate.GetInvocationList())
{
eventInfo.RemoveEventHandler(objectWithEvents, _delegate);
}
}
}
}
}