views:

108

answers:

3

Hello.

Simple situation(VS2005, .NET2): I have a textBox1 on a panel1. I need to track all the emitted events from this textBox1.

Actually, when I need to track a single event(TextChanged, by e.g.) from this textBox1 I add a handler to the (TextChanged event) I set then a non-blocking breackpoint that writes on the output console a message containing the function (eventhandler) name. So, for a single event I can write this handler and set the breackpoint, but can I do something similar for all possible textbox(or other control)'s events?

alt text

More deeply, I need to know what event have place in a certain moment on the mentioned control (textBox).

Thanks.

A: 

You can't do this automatically.

However, if you're just trying to figure out which events get fired when, try Spy++.

SLaks
does it intercept ALL type of events, or just the system ones?
serhio
It displays Windows messages; almost all standard events map directly to Windows messages.
SLaks
+1  A: 

You could use reflection to get all the events of the object and hook them all into a debug method of yours.

void HookAllEvents(object obj)
{
    foreach (var evInfo in obj.GetType().GetEvents())
    {
        evInfo.AddEventHandler(obj, new EventHandler(DebugEventHandler));
    }
}

void DebugEventHandler(object sender, EventArgs e)
{
}

Don't forget to unhook at the end :)

Aviad P.
This won't work for events like `KeyPress` that aren't `EventHandler`s.
SLaks
Looks to me like it's a normal event handler. http://msdn.microsoft.com/en-us/library/system.windows.forms.keypresseventhandler.aspx
Aviad P.
KeyPress is a `KeyPressEventHandler`, not an `EventHandler`.
SLaks
so, how can I know what event is passed to the DebugEventHandler? (don't forget about .NET **2** )
serhio
Check `Environment.StackTrace`.
SLaks
`KeyPressEventHandler` is contravariant to `EventHandler` because `KeyPressEventArgs` derives from `EventArgs`.
Aviad P.
I used( **.NET 2**) `EventInfo evInfo`.. So.. Runtime error: `Impossible to convert an objet of type 'System.EventHandler' in types System.Windows.Forms'MouseEventHandler, PaintEventHandler,ControlEventHandler,DragEventHandler,GiveFeedbackEventHandler, HelpEventHandler,InvalidateEventHandler,QueryContinueDragEventHandler, QueryAccessibilityHelpEventHandler,KeyEventHandler,KeyPressEventHandler,LayoutEventHandler, PreviewKeyDownEventHandler`
serhio
+3  A: 

Aviad's post is a good start. You can use it for events of type EventHandler. For other delegate types you can use the same technique creating handler for each type manually.

If you want to be more flexible you should create event handler in runtime using System.Reflection.Emit. Here is a detailed explanation: http://msdn.microsoft.com/en-us/library/ms228976.aspx. Scroll down to "To generate an event handler at run time by using a dynamic method" title.

//EDIT

I created simple class which is able to handle all events of particular object and pass them to universal event handler. Code is based on examples from Microsoft and XTreme.NET Talk.

Basic idea

  • create method in runtime which has the same arguments as event
  • set this mehod as event handler for each event in Type.GetEvents()
  • from this method call universal event handler

Usage

Object to attach to is passed in constructor. Next anonymous method of type UniversalEventHandler is used to handle all events. This method receives event name as eventName and event arguments in args. You can set breakpoint on this method and inspect arguments in Visual Studio or print them by yourself. In this sample usage only event name is printed and can be found in Output window (menu View > Output in Visual Studio). In this example standard button button1 is examined.

private void Form1_Load(object sender, EventArgs e)
{            
    UniversalEventHandler handler = (UniversalEventHandler) delegate(string eventName, object[] args)
    {
        System.Diagnostics.Trace.WriteLine(eventName);
    };

    EventInspector inspector = new EventInspector(button1, handler);

}

Code

public delegate void UniversalEventHandler(string eventName, object[] args);

public class EventInspector
{
    private UniversalEventHandler eventHandler;
    private object srcObject;

    public EventInspector(object srcObject, UniversalEventHandler eventHandler)
    {
        this.eventHandler = eventHandler;
        this.srcObject = srcObject;
        Attach();
    }

    public void EventReceived(string eventName, object[] args)
    {
        if (eventHandler != null)
            eventHandler(eventName, args);
    }

    public void Attach()
    {
        Type type = srcObject.GetType();
        EventInfo[] srcEvents = type.GetEvents();

        for (int i = 0; i < srcEvents.Length; i++)
        {
            EventInfo srcEvent = srcEvents[i];

            Type[] parameterTypes = GetDelegateParams(srcEvent.EventHandlerType);
            DynamicMethod handler = new DynamicMethod("", typeof(void), parameterTypes, typeof(EventInspector));
            string name = srcEvent.Name;
            ILGenerator il = handler.GetILGenerator();
            il.DeclareLocal(typeof(object[]));
            il.DeclareLocal(typeof(string));

            il.Emit(OpCodes.Ldstr, srcEvent.Name);
            il.Emit(OpCodes.Stloc_1);
            il.Emit(OpCodes.Ldc_I4, parameterTypes.Length - 1);
            il.Emit(OpCodes.Newarr, typeof(object));
            il.Emit(OpCodes.Stloc_0);

            for (int j = 0; j < parameterTypes.Length - 1; j++)
            {
                Type parameter = parameterTypes[j];
                il.Emit(OpCodes.Ldloc_0);
                il.Emit(OpCodes.Ldc_I4, j);
                il.Emit(OpCodes.Ldarg, j + 1);
                il.Emit(OpCodes.Stelem_Ref);
            }

            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldloc_1);
            il.Emit(OpCodes.Ldloc_0);

            MethodInfo eventReceivedMethod = this.GetType().GetMethod("EventReceived", BindingFlags.Public | BindingFlags.Instance);
            il.EmitCall(OpCodes.Callvirt, eventReceivedMethod, null);
            il.Emit(System.Reflection.Emit.OpCodes.Ret);                                 

            srcEvent.AddEventHandler(srcObject, handler.CreateDelegate(srcEvent.EventHandlerType, this));
        }
    }

    private Type[] GetDelegateParams(Type d)
    {
        MethodInfo delegateMethod = d.GetMethod("Invoke");
        ParameterInfo[] delegateParams = delegateMethod.GetParameters();

        Type[] result = new Type[delegateParams.Length + 1];
        result[0] = this.GetType();

        for (int i = 0; i < delegateParams.Length; i++)
        {
            result[i + 1] = delegateParams[i].ParameterType;
        }

        return result;
    }
}
Petr Havlicek
@Petr: First of all, the Aviad method, unfortunately, does not work for me(.NET 2), because it cannot convert 'System.EventHandler' in the 'System.Windows.Forms.MouseEventHandler'. Secondary, I don't need to *generate* an event, but to *intercept* it...
serhio
Awesome class!! Surely useful in a million scenarios.
Dested
Serhio: Using code in my edited answer you can intercept all events and its arguments. Aviad's method works for me under .NET 2.0, please try this code: this.GetType().GetEvent("KeyDown").AddEventHandler(this, (EventHandler)delegate(object src, EventArgs ev) {... });It will does nothing because KeyDown is not EventHandler, but it will compile with no errors. If you change "KeyDown" to "Click" handler will be triggered.Hope this helps.
Petr Havlicek
@Petr: is it your own code, or a "general" practice? :) This is really useful, even if I don't understand everything
serhio
I have an idea and started coding. I run into some problems with IL generation so I use one from XTreme.NET and added eventName passing. So it is not all mine. Some improvements could be done like creating only one method for each delegate type. However for getting the job done this could be used. I think about creating some kind of blog post where I could more explain how this code works. Do you want me to let you know if this happnes?
Petr Havlicek
@Petr: Yeah, I'll visit your blog with pleasure.
serhio