2

カスタム イベントの場合、次のようにハンドラーを確認できます。

 If Object.EventNameEvent Is Nothing Then
    MsgBox("Is not handling it.")
 End If

...しかし、たとえば、デザイナーで生成されたボタンの「.click」イベントをチェックするには、どうすれば同じことができますか? これは動作しません:

If Button1.ClickEvent Is Nothing Then
   MsgBox("Is not handling it.")
End If

アップデート

私の要件の例:

    MsgBox(HasAttachedHandler(MySub, Button1.Click))  ' Expected result: True
    MsgBox(HasAttachedHandler(MyFunc, Button1.Click)) ' Expected result: False

Private Sub MySub() Handles Button1.Click, Button2.Click
    ' bla bla bla
End Sub

Private Function MyFunc() Handles Button2.Click
    ' bla bla bla
End Function

更新 2:

@ varocarbasソリューションを使用しようとしていますが、必要なことを正確に行っていないため、機能させるために必要な変更を加えようとしました。ここでわかるように、問題はイベント「FontChaged」が目的の結果を返さないことです。

    Public Class Form1

    Private WithEvents Button1 As New Button

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        ' This is working (Result True):
        MsgBox(HasAttachedHandler(Button1, "Click", "Button1_Click")) ' Result: True

        ' This is not working (Result False):
        MsgBox(HasAttachedHandler(Button1, "FontChanged", "Button1_Click")) ' Expected result: True
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles _
                Button1.Click, _
                Button1.MouseHover, _
                Button1.GotFocus, _
                Button1.Enter, _
                Button1.FontChanged, _
                Button1.AutoSizeChanged

    End Sub

    Private Function HasAttachedHandler(ByVal ctl As Control, ByVal eventname As String, ByVal targetMethod As String) As Boolean

        For Each evnt In ctl.GetType().GetEvents()

            ' Get secret key for the current event:
            Dim curEvent As Reflection.FieldInfo = GetType(Control).GetField("Event" & evnt.Name, Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Static)

            If (curEvent IsNot Nothing) Then
                Dim secret As Object = curEvent.GetValue(Nothing)

                ' Retrieve the current event:
                Dim eventsProp As Reflection.PropertyInfo = GetType(System.ComponentModel.Component).GetProperty("Events", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)
                Dim events As System.ComponentModel.EventHandlerList = DirectCast(eventsProp.GetValue(ctl, Nothing), System.ComponentModel.EventHandlerList)

                If (Not IsNothing(events(secret))) AndAlso curEvent.Name.ToLower = "event" & eventname.ToLower Then
                    Dim handler As [Delegate] = events(secret)
                    Dim method As Reflection.MethodInfo = handler.Method
                    If (targetMethod = method.Name) Then Return True
                End If
            End If
        Next

        Return False
    End Function

End Class
4

4 に答える 4

1

.NETフレームワークでこの種のことを処理する、私なりの型破りな方法を紹介します。ご質問から...

...デザイナで生成されるボタンの「クリック」イベント...

したがって、これは のサブクラスにのみ適用されると仮定しSystem.Windows.Forms.Controlます。他の多くの人が実証したように、 はControlを使用してイベント システムを少し異なる方法で管理しますEventHandlerList。たとえば、KeyDownイベントでは、次のように実装されます...

Public Custom Event KeyDown As KeyEventHandler
    AddHandler(ByVal value As KeyEventHandler)
        MyBase.Events.AddHandler(Control.EventKeyDown, value)
    End AddHandler
    RemoveHandler(ByVal value As KeyEventHandler)
        MyBase.Events.RemoveHandler(Control.EventKeyDown, value)
    End RemoveHandler
End Event

EventKeyDown後でハンドラーを取得するために使用される「キー」オブジェクトとして定義されている場所

Private Shared ReadOnly EventKeyDown As Object

よし、ここまではいいぞ!Idle_Mind と varocarbas の両方が解決策を打ち出しましたがFontChanged、提案された方法ではイベントが機能しないようだとおっしゃいました。なんで?どちらのメソッドもフィールド オブジェクトがあると想定するためEventFontChanged、実際には次のように定義されます。

Private Shared ReadOnly EventFont As Object

今、仮定することは明らかです...

GetField("Event" + eventName)

...まったく機能しません。

すべてのイベントはControlクラスで定義されているため、そこから直接反映できます。オブジェクト キーを見つけるには、イベント ハンドラから「フィールド参照」を見つける必要があります (KeyDown上記の実装例を参照してください)。CILバイト コードを取得し、静的フィールド参照 ( ) をロードするオペコードが見つかるまでループしますldsfld。のオペランドはldsfld、型に解決できる 32 ビット整数です。Idle_Mind と varocarbas の両方が既に示しているように、残りは簡単です。

Imports System.Reflection
Imports System.ComponentModel
Imports System.Reflection.Emit

Public Class Form1

    Private Shared Sub OnFooEvent(sender As Object, e As EventArgs) Handles Foo.Click, _
                Foo.MouseHover, _
                Foo.GotFocus, _
                Foo.Enter, _
                Foo.FontChanged, _
                Foo.AutoSizeChanged

    End Sub

    Private Sub OnWindowLoad(sender As Object, e As EventArgs) Handles MyBase.Load
        MsgBox(HasHandlers(Foo, "AutoSizeChanged"))
    End Sub

    Private Function HasHandlers(ByVal instance As Object, ByVal arg As String) As Boolean
        Dim baseType = GetType(Control)
        Dim eventInfo = baseType.GetEvent(arg)

        Dim method = eventInfo.AddMethod.GetMethodBody().GetILAsByteArray()
        Dim id As Integer

        For i As Integer = 0 To method.Length - 1
            If method(i) <> OpCodes.Ldsfld.Value Then Continue For
            i += 1
            Dim operand(3) As Byte
            Buffer.BlockCopy(method, i, operand, 0, 4)
            id = BitConverter.ToInt32(operand, 0)
            Exit For
        Next

        If id = 0 Then Return False

        Dim key = baseType.Module.ResolveField(id).GetValue(instance)
        Dim listInfo = GetType(Component).GetProperty("Events", BindingFlags.NonPublic Or BindingFlags.Instance)

        Dim list = listInfo.GetValue(instance)
        Return list(key) IsNot Nothing
    End Function
End Class

各バイトコードのオペランドサイズをまだ考慮していないため、このコードは安全ではありません! したがって、ループは誤ってオペコード以外のバイトを拾い上げ、それをldsfld命令と間違える可能性があります。

于 2013-06-21T12:19:16.247 に答える