5

Is it possible in VB.NET to easily write an event handler that will handle every event that fires? I'm wondering if one could make a logging system using something like this.

I'm wanting to do something like (in pseudocode):

Public Sub eventHandledEvent(ByVal sender As Object, ByVal e As EventArgs)
    File.Write(sender.EventName)
End Sub

I realize it would be slow, but it wouldn't be for a production system, only as a development tool.

davidscolgan
  • 7,508
  • 9
  • 59
  • 78
  • Can you supply a code example of what you are looking to do? – Oded Jul 19 '10 at 18:55
  • 3
    If you handle every event in VB, you will slow your system to a crawl. WM_PAINT fires thousands of times per second for instance. – Byron Whitlock Jul 19 '10 at 18:55
  • I wasn't even sure if it is feasible, but it could be useful for debugging purposes when performance doesn't matter too much. – davidscolgan Jul 19 '10 at 18:56
  • It should be possible to do this with reflection? Find all the events, and add handlers for them all? Anyone have any code for that. – MarkJ Jul 20 '10 at 10:43
  • Obviously no one does. OK, I will have to answer the question myself :) – MarkJ Jul 21 '10 at 19:47

2 Answers2

5

You can do this with reflection. Here's how. Create a form with a textbox called TextBox1. Paste the following code. Run the project and look at the immediate window.

Public Class Form1

  Private Sub Form1_Activated(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Activated
    RegisterAllEvents(TextBox1, "MyEventHandler")
  End Sub

  Sub MyEventHandler(ByVal sender As Object, ByVal e As EventArgs)
    Debug.WriteLine("An event has fired: sender= " & sender.ToString & ", e=" & e.ToString)
  End Sub
  Sub RegisterAllEvents(ByVal obj As Object, ByVal methodName As String)
    'List all events through reflection'
    For Each ei As System.Reflection.EventInfo In obj.GetType().GetEvents()
      Dim handlerType As Type = ei.EventHandlerType
      Dim method As System.Reflection.MethodInfo = Me.GetType().GetMethod(methodName)
      'Create a delegate pointing to the method'
      Dim handler As [Delegate] = [Delegate].CreateDelegate(handlerType, Me, method)
      'Register the event through reflection'
      ei.AddEventHandler(obj, handler)
    Next
  End Sub
End Class

This is from Francesco Balena's book Programming Microsoft Visual Basic 2005 The Language. The techique works with any object that raises events, not just controls. It uses contravariance.

If you buy the book, there's a full explanation and some more code which will allow you to identify which event has fired in the universal handler, and use regular expressions to handle only a subset of events. I don't feel I can post such a long excerpt here.

Community
  • 1
  • 1
MarkJ
  • 30,070
  • 5
  • 68
  • 111
3

Edit: Adjusted due to Hans' comment.

No problem, at least for some events, since it's already built in for the events that send out messages. Just look at Control.WndProc. All messages to the window will go through there.

Hans Olsson
  • 54,199
  • 15
  • 94
  • 116
  • +1 spot on. However for any child controls you will have to subclass them, then use the subclassed versions. E.g., Public Class MyTextBox/Inherits Textbox/Overrides WndProc (code...)/end class. CALL A WORKER SUB don't put code in the WndProcs. Pass it the Message and control.name. FILTER THE MESSAGES based on what you care about; most, you wont. – FastAl Jul 19 '10 at 19:44
  • Not really. Lots of events don't have a Window message associated with them. AutoSizeChanged, BackColorChanged, BindingContextChanged, etc, etc. But it's about as practical as it gets. – Hans Passant Jul 19 '10 at 19:45
  • Obviously this only covers controls not *all* events. – dr. evil Jul 19 '10 at 20:26
  • This technique works, for controls which have a window, but why not just use Spy++ or Winspector? http://www.softpedia.com/get/Security/Security-Related/Winspector.shtml Why reinvent the wheel? – MarkJ Jul 20 '10 at 10:46