Event name from EventHandler or Delegate?

G

Gnarlito

I have an application in which I need to fire an event from some class
other than the one that has defined the event. I am trying to do this
by finding the delegate associated with the event in question, and then
by calling GetInvocationList() and invoking each EventHandler in turn.
And although I've found some nice links to back-door techniques on how
to retrieve a delegate for a given event, those techniques don't seem
to work for me. I've figured out an alternative method, but it's not
quite working the way I want it to...yet.

Alex Feinman
(http://www.opennetcf.org/PermaLink.aspx?guid=4cf118ee-5668-452e-af2c-f3a0c6d43a7f)
and others have suggested a technique where you cycle through the
private fields of a type looking for one that has the same name as the
event of interest. For example, to find the delegate for the Click
event for button1, and then emulate the button-click event on that
button:

Type t = typeof(Control);
FieldInfo fi = t.GetField("Click", BindingFlags.NonPublic |
BindingFlags.Instance);
Delegate handler = fi.GetValue(button1) as Delegate;
foreach (Delegate d in handler.GetInvocationList())
{ d.Invoke(button1, new EventArgs()); }

Only trouble is, this doesn't work for me. I can't find this private
field. What "almost" works instead is something like the following, but
it returns *all* delegates, and by, extension, all event handlers, for
the associated control, not just the ones associated with the Click
event or some other event of interest. So my question is, given an
instance of an EventHandler or Delegate, is it possible to determine
which Event that EventHandler or Delegate is associated with?

// button1 is defined as a Button, and it has added an EventHandler
// to the chain of handlers that handle the Click event. It has also
// defined some handlers to handle other events and has added those
// handlers to the chain of handlers associated with those other
// events. Every non-null delegate for the control is returned by
// findDelegates(), not just the Button.Click event delegate.
ArrayList allDelegates = findDelegates(button1);

// The following will cycle through ALL delegates. not just the ones
// associated with the Click event. This is not good....
foreach (Delegate del in allDelegates)
{
Delegate[] theseDelegates = del.GetInvocationList();
foreach (EventHandler thisHandler in theseDelegates)
{
thisHandler.Invoke(buttonDemo, new EventArgs());
}
}

// What I would like to do:
// Delegate buttonClickDelegate = findDelegateFor(button1, "Click");
// and then invoke this delegate's handlers as above.
....

protected ArrayList findDelegates(object o)
{
ArrayList rc = new ArrayList();

// Event-handler information for a given instance
// of an object should be accessible through a private field
// of type EventHandlerList. And this field probably won't
// be defined for the current class type of object o.
// The field will probably be defined as part of a base
// class that is buried several classes deep in the
// inheritance chain.

// findFieldType will recurse through the type tree for
// the caller-specified object until it finds a field of type
// EventHandlerList
FieldInfo f = findFieldType(o.GetType(),
typeof(EventHandlerList));
if (null == f)
{ return rc; }

EventHandlerList ehl = (EventHandlerList)f.GetValue(o);
if (null == ehl)
{ return rc; }

// The EventHandlerList class doesn't expose what we need.
// We need to get to the private fields...to the contents of the
// linked list that is contained within the class. Each entry in
// the linked list is of type ListEntry (a private nested class
// apparently defined within EventHandlerList) and represents
// one delegate. The event handlers for that delegate can then
// be accessed using the publicly visible GetInvocationList()
// method.
FieldInfo headFieldInfo = ehl.GetType().GetField("head",
BindingFlags.NonPublic | BindingFlags.Instance);
if (null == headFieldInfo)
{ return rc; }

object headField = headFieldInfo.GetValue(ehl);

// findDelegate will populate the caller-supplied ArrayList with
// all delegates in the linked listed headed by headField.
// What we really need is a way to get only those delegates
// associated with a specific event.
findOneDelegate(headField, ref rc);
return rc;
}

/// <summary>
/// Walk through the linked list represented by the caller-supplied
/// ListEntry object and extract delegates from that list.
/// Each event exposed by a given control is represented by one linked
/// list entry. There may be multiple delegates to handle the event,
/// but those delegates are retrieved using the GetInvocationList()
/// method and not by using this back-door stuff.
/// </summary>
/// <param name="o">A linked-list entry of type ListEntry</param>
/// <param name="delegateList">A reference to a delegate list ArrayList

/// that is recursively populated</param>
protected void findOneDelegate(object o, ref ArrayList delegateList)
{
if (null == o)
{ return; }
object next = null;
foreach (FieldInfo fx in o.GetType().GetFields(
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance))
{
object thisObj = fx.GetValue(o);

// thisObj is of type ListEntry. It has 3 fields:
// next -- the next ListEntry in the linked list,
// key -- an object. Obviously this identifies the event, but how??
// delegate -- a delegate
if (fx.Name.ToLower() == "next")
{ next = thisObj; }

if (fx.Name.ToLower() == "key")
{
Trace.WriteLine("Key type: " + thisObj.GetType().Name +
"; hashcode: " + thisObj.GetHashCode().ToString());
}

if (fx.FieldType.Name.ToLower() == "delegate")
{ delegateList.Add((Delegate)thisObj); }
}

if (next != null)
{ findOneDelegate(next, ref delegateList); }
}
 
G

Guest

be carefule there is a valid caveat in htat hread about using this approach.
will work in most situations but it is not fail safe
 
J

Jon Skeet [C# MVP]

Blair Allen Stark said:
be carefule there is a valid caveat in htat hread about using this approach.
will work in most situations but it is not fail safe

I browsed the thread but couldn't find anything there about the
assumption which was being made about the events - that they were
backed by a field of the same name.

Not all events are backed that way - some aren't even backed by a
straight delegate field. For instance, I believe Control handles its
events by having them in a dictionary keyed by an identifier associated
with the event. That means that if event handlers haven't been added
for a particular event, those events don't take any memory.
 
G

Gnarlito

Thanks, Jon. I appreciate the heads up.

I dug a little deeper and pretty much confirmed what you've said. While
I haven't yet found an event that isn't represented in some way by a
private static field of type object at some level in the Type
inheritance tree for a given Component or Control, there isn't a
standard naming convention for these fields from type to type. While I
found a core set of static private fields named using the convention
"Event{eventHame}" in Control to represent the events that are common
to all controls, ComboBox (for example) represents its
SelectedIndexChanged event with a private static field called
EVENT_SELECTEDINDEXCHANGED.

I'm pretty sure that the dictionary you are referring to is maintained
as private field Component.events, which is type EventHandlerList.
 
G

Gnarlito

....Epilogue

I'm hoping that my back-door simulated event-firing hack will be so
much dust on the trail when Microsoft releases Visual Studio Tools for
Applications (VSTA) later this year. VSTA is supposed to allow you to
integrate a scripting and macro-recording capability into a Windows
Forms app, which is all I really want to implement.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top