Determine list of event handlers bound to event
Asked Answered
S

2

36

I have a WinForms form that won't close. In OnFormClosing, e.Cancel is set to true. I am guessing that some object in my application has bound to the Closing or FormClosing event, and is blocking the close. To find out, I'd like to determine what delegates are bound to one of these events.

Is there a way to determine the list of handlers bound to an event? Ideally I would do this via the Visual Studio debugger, but can write code in the application to find the handlers if necessary. Understanding that an event is like a hidden private field, I've navigated through the Debugger to the "Non-Public Fields" for the "Windows.Forms.Form" ancestor of my form, but to no avail.

Surfboat answered 18/3, 2009 at 23:24 Comment(0)
D
38

In short, you're not meant to do this - but for debugging purposes...

An event is often backed by a private field - but not with controls; they use the EventHandlerList approach. You would have to access the form's protected Events member, looking for the object mapped to the (private) EVENT_FORMCLOSING object.

Once you have the FormClosingEventHandler, GetInvocationList should do the job.


using System;
using System.ComponentModel;
using System.Reflection;
using System.Windows.Forms;
class MyForm : Form
{
    public MyForm()
    { // assume we don't know this...
        Name = "My Form";
        FormClosing += Foo;
        FormClosing += Bar;
    }

    void Foo(object sender, FormClosingEventArgs e) { }
    void Bar(object sender, FormClosingEventArgs e) { }

    static void Main()
    {
        Form form = new MyForm();
        EventHandlerList events = (EventHandlerList)typeof(Component)
            .GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance)
            .GetValue(form, null);
        object key = typeof(Form)
            .GetField("EVENT_FORMCLOSING", BindingFlags.NonPublic | BindingFlags.Static)
            .GetValue(null);

        Delegate handlers = events[key];
        foreach (Delegate handler in handlers.GetInvocationList())
        {
            MethodInfo method = handler.Method;
            string name = handler.Target == null ? "" : handler.Target.ToString();
            if (handler.Target is Control) name = ((Control)handler.Target).Name;
            Console.WriteLine(name + "; " + method.DeclaringType.Name + "." + method.Name);
        }
    }
}
Drizzle answered 18/3, 2009 at 23:29 Comment(2)
I had the same need; in Control, the identifying keys are named in fashion like EventMouseDown rather than EVENT_MOUSEDOWN as they are for Form.Gregggreggory
For anyone who is trying this with a bunch of Windows controls and can't figure out why CheckBox.CheckedChanged and CheckBox.CheckStateChanged don't work, see https://mcmap.net/q/21282/-issue-when-trying-to-get-the-delegate-associated-to-a-control-39-s-eventDeannadeanne
S
1

The problem might be that the form doesn't validate.

The FormClosing event is raised by the private WmClose method in Form, which initializes e.Cancel to !Validate(true). I haven't investigated, but in certain circumstances, Validate will always return false, causing the close to be canceled regardless of any event handlers.

To investigate this, enable .Net source debugging, put a breakpoint in your FormClosing handler, go to the source for Form.WmClose (up the call stack), put a breakpoint at the beginning of WmClose, and close the form again. Then, step through it in the debugger and see why Validate is returning false. (Or which event handler is setting e.Cancel to true)

To solve the problem, set e.Cancel to false in your own handler.

Stipitate answered 26/6, 2009 at 2:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.