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

2

这是我对@HansPassant 发布的代码进行转换和修改的尝试:

Imports System.Reflection
Imports System.ComponentModel
Public Class Form1

    Private WithEvents btnA As New Button
    Private WithEvents btnB As New Button

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        btnA.AutoSize = True
        btnA.Text = "Handler Attached"
        btnA.Location = New Point(10, 10)
        Me.Controls.Add(btnA)

        btnB.AutoSize = True
        btnB.Text = "No Handlers Attached"
        Dim pt As Point = btnA.Location
        pt.Offset(btnA.Width, 0)
        btnB.Location = pt
        Me.Controls.Add(btnB)
    End Sub

    Private Sub btnA_Click(sender As Object, e As System.EventArgs) Handles btnA.Click
        Dim btnA_Handled As Boolean = HasAttachedHandler("Click", btnA)
        Dim btnB_Handled As Boolean = HasAttachedHandler("Click", btnB)

        Debug.Print("btnA_Handled = " & btnA_Handled)
        Debug.Print("btnB_Handled = " & btnB_Handled)
    End Sub

    Private Function HasAttachedHandler(ByVal EventName As String, ByVal ctl As Control) As Boolean
        ' Get secret click event key
        Dim eventClick As FieldInfo = GetType(Control).GetField("Event" & EventName, BindingFlags.NonPublic Or BindingFlags.Static)
        Dim secret As Object = eventClick.GetValue(Nothing)
        ' Retrieve the click event
        Dim eventsProp As PropertyInfo = GetType(Component).GetProperty("Events", BindingFlags.NonPublic Or BindingFlags.Instance)
        Dim events As EventHandlerList = DirectCast(eventsProp.GetValue(ctl, Nothing), EventHandlerList)
        Return Not IsNothing(events(secret))
    End Function

End Class
于 2013-06-09T22:08:14.140 回答
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

whereEventKeyDown被定义为稍后用于检索处理程序的“关键”对象

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 回答
0

首先,我想强调一下 Hans Passant 的代码非常出色,并且在一个很好的起点下,大部分工作已经完成。我已经使用了 Idle_Mind 代码的一部分并做了一些研究,在这里你有你正在寻找的东西:

Imports System.Reflection
Imports System.ComponentModel
Public Class Form1

    Private WithEvents Button1 As New Button
    Private Sub Button1_Click(sender As Object, e As System.EventArgs) Handles Button1.Click

    End Sub

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

        Dim allEvents0 = ctl.GetType().GetEvents()

        For Each evnt In allEvents0
            ' Get secret key for the current event
            Dim curEvent As FieldInfo = GetType(Control).GetField("Event" & evnt.Name, BindingFlags.NonPublic Or BindingFlags.Static)
            If (curEvent IsNot Nothing) Then
                Dim secret As Object = curEvent.GetValue(Nothing)
                ' Retrieve the current event
                Dim eventsProp As PropertyInfo = GetType(Component).GetProperty("Events", BindingFlags.NonPublic Or BindingFlags.Instance)
                Dim events As EventHandlerList = DirectCast(eventsProp.GetValue(ctl, Nothing), EventHandlerList)

                If (Not IsNothing(events(secret))) Then
                    Dim handler As [Delegate] = events(secret)
                    Dim method As MethodInfo = handler.Method
                    If (targetMethod = method.Name) Then
                        Return True
                    End If
                End If
            End If
        Next

   End Function

   Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
          Dim isTheMethodUsed As Boolean = HasAttachedHandler2(Button1, "Button1_Click")
   End Sub

End Class
于 2013-06-14T10:57:18.183 回答
0

在做了一些研究、测试并耗尽了我在这方面的知识和免费可用的信息之后,我不得不放弃所要求的修复。以下是我的结论:

  • 原始方法(来自 Hans Passant)的局限性在于FieldInfo假设每个事件都有一个关联的Field但情况似乎并非如此
  • 另一方面,EventHandlerList似乎确实包括了所有事件。不幸的是,它是一个特殊的字典,其中的值只能通过Object定义给定的 an 来检索Event(在(转换的)Hans Passant 代码中:)Dim secret As Object = curEvent.GetValue(Nothing)
  • 通过假设上述两点是正确的(互联网上没有太多信息),事件不应该是FieldInfo,而是EventInfoDim curEvent As EventInfo = GetType(Control).GetEvent(evnt.Name))。除了secret变量的计算以及events. 令人惊讶的是,这个问题的解决方案并不容易:显然,只能secret提供正确的索引(?!)。
  • 我发现了似乎是潜在的替代方案:要么从EventHandlerList没有secret object这一行的代码)中提取信息;或将secret object/替换EventHandlerList为其他变量,例如PropertyDescriptorCollectionEventDescriptorCollectionMethodInfo.

如果有人可以纠正/扩展这篇文章以帮助(我或其他人)解决这个问题,那就太好了。

于 2013-06-15T14:00:31.220 回答