イベントハンドラを取得する - C#

イベントにフックされているイベントハンドラを取得する方法

例えば、フォームにbutton1というボタンがあり、button1のクリックイベントにフックされているイベントハンドラを取得して、何らかの処理を行いたい場合があります。
そのようなことを行うにはSystem.Reflectionを使用します。
using System;
using System.ComponentModel;
using System.Reflection;

namespace WindowsFormsApplication1
{
    public class EventDatum
    {
        public static Delegate GetEventHandler(object obj, string eventName)
        {
            EventHandlerList ehl = GetEvents(obj);
            object key = GetEventKey(obj, eventName);
            return ehl[key];
        }



        private delegate MethodInfo delGetEventsMethod(Type objType, delGetEventsMethod callback);
        private static EventHandlerList GetEvents(object obj)
        {
            delGetEventsMethod GetEventsMethod = delegate(Type objtype, delGetEventsMethod callback)
            {
                MethodInfo mi = objtype.GetMethod("get_Events", All);
                if ((mi == null) & (objtype.BaseType != null))
                    mi = callback(objtype.BaseType, callback);
                return mi;
            };

            MethodInfo methodInfo = GetEventsMethod(obj.GetType(), GetEventsMethod);
            if (methodInfo == null) return null;
            return (EventHandlerList)methodInfo.Invoke(obj, new object[] { });
        }



        private delegate FieldInfo delGetKeyField(Type objType, string eventName, delGetKeyField callback);
        private static object GetEventKey(object obj, string eventName)
        {
            delGetKeyField GetKeyField = delegate(Type objtype, string eventname, delGetKeyField callback)
            {
                FieldInfo fi = objtype.GetField("Event" + eventName, All);
                if ((fi == null) & (objtype.BaseType != null))
                    fi = callback(objtype.BaseType, eventName, callback);
                return fi;
            };

            FieldInfo fieldInfo = GetKeyField(obj.GetType(), eventName, GetKeyField);
            if (fieldInfo == null) return null;
            return fieldInfo.GetValue(obj);
        }



        private static BindingFlags All
        {
            get
            {
                return
                    BindingFlags.Public |
                    BindingFlags.NonPublic |
                    BindingFlags.Instance |
                    BindingFlags.IgnoreCase |
                    BindingFlags.Static;
            }
        }
    }
}

なぜget_Eventsなのか

取得したいのはEventHandlerList型の変数名がEventsというオブジェクトなのですが、何故get_Eventsというメソッドを取得しているのでしょうか?
ILDASM.exeでILコード見てみると以下の一行を見ることができます。
IL_0001:  call       instance class [System]System.ComponentModel.EventHandlerList [System]System.ComponentModel.Component::get_Events()
つまり、ILコードでは「get_ + 元変数名」という名のメソッドになっているからです。

Keyの変数名は「"Event" + イベントネーム」

.Net Frameworkにある既存のコンポーネントに使用されているキー名の多くがこの形となります。
詳しくは「イベントハンドラの取得に使われるキー名の一覧 - C#」を参照下さい。

その他のポイント

GetEventsメソッド
return (EventHandlerList)methodInfo.Invoke(obj, new object[] { });
コードを見るとGetType()とあるように型からメソッドを取得しています。しかし、中身はインスタンスにあります。ですから、MethodInfoのInvokeメソッドを使用して現在のインスタンスによって表されるメソッドまたはコンストラクタを呼び出しています。

GetEventKeyメソッド
return fieldInfo.GetValue(obj);
指定したオブジェクトのフィールドの値を返しています。

使用例

以下の例ではbutton2をクリックするとbutton1のクリックイベントにフックされたイベントハンドラを呼び出しています。
private void button1_Click(object sender, EventArgs e)
{
    try
    {
        Button btn = sender as Button;
        MessageBox.Show(btn.Name + " Clicked.");
    }
    catch
    {
        MessageBox.Show("sender Is Not Button.");
    }
}

private void button2_Click(object sender, EventArgs e)
{
    Delegate handler = EventDatum.GetEventHandler(button1, "Click");

    if (handler != null)
    {
        handler.DynamicInvoke(new object[] { sender, e });
    }
}

参考:
http://www.technewsgroups.net/group/microsoft.public.dotnet.general/topic4196.aspx

0 Comments:

Sony Style(ソニースタイル)
デル株式会社

Recent Posts