Search code examples
c#.neteventsmessage-loop

.NET's Event Mechanism


In all the books regarding C#/.NET that I have seen till now, when they talk about Events,
they talk about Creating and Consuming Events.

I am curious to understand a bit how it works behind our code - what is the mechanism that runs it.

I know a little bit from the fact that the Windows Message Loop is acting like a Queue for the events that are raised.
For example, WM_KEYDOWN, or WM_LBUTTONDOWN, etc..

But what happens for example, If I create a class that does not inherit class Control, and this class raises an event?
(which another class, which also does not inherit class Control, receives it)

Will the raised event go thru the message loop?
It sound not so logic..
(yet assume that the project is a Windows Forms project, just the 2 classes - the sender and the receiver are not GUI classes at all, but simple classes that you wrote)

Any explanation or link to an article about the mechanism behind our code will be highly appreciated.


Solution

  • I hope I'm understanding your question correctly. I think we're talking about two things.

    First - how events work in C# Second - how a WinForms application written in C# knows when you've clicked a button.

    Events in C# are their own distinct thing. You can write a console app, create your own event, listen to it, fire it, respond to it, etc... and it all just works. You subscribe to an event by called Add() and you unsubscribe by calling Remove(). The event itself keeps track of what methods are listening to it and when it's raised, calls all of those methods.

    Jon Skeet explains it much better: How do C# Events work behind the scenes?

    But these events are just C# code. Related to, but distinct from the Win32 messages you're mentioning. In a Winforms application, when the user clicks a button, how does the application know about it? We can look using the debugger (turn off the 'My Code' https://msdn.microsoft.com/en-us/library/dn457346.aspx option) and set a breakpoint in the click event, you'll be able to see what is going on.

    Callstack

    So in Windows.Forms.Controls.ControlNativeWindow there is a WndProc method that takes in a System.Windows.Forms.Message m.

    Right before that is a 'debuggableCallback' method. That mirrors what you'd expect from a Win32API app.

    Source here: http://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/NativeWindow.cs,ad40308c5b6490dd

    /// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.DebuggableCallback"]/*' />
    /// <devdoc>
    ///     Window message callback method. Control arrives here when a window
    ///     message is sent to this Window. This method packages the window message
    ///     in a Message object and invokes the wndProc() method. A WM_NCDESTROY
    ///     message automatically causes the releaseHandle() method to be called.
    /// </devdoc>
    /// <internalonly/>
    private IntPtr DebuggableCallback(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam) {
    
        // Note: if you change this code be sure to change the 
        // corresponding code in Callback above!
    
        Message m = Message.Create(hWnd, msg, wparam, lparam);
    
        try {
            if (weakThisPtr.IsAlive && weakThisPtr.Target != null) {
                WndProc(ref m);
            }
            else {
                DefWndProc(ref m);
            }
        }
        finally {
            if (msg == NativeMethods.WM_NCDESTROY) ReleaseHandle(false);
            if (msg == NativeMethods.WM_UIUNSUBCLASS) ReleaseHandle(true);
        }
    
        return m.Result;
    }
    

    So, ultimately, if you're running on Windows, it's driven by the same Win32 API messages you'd expect. It's just that the System.Windows.Forms classes are written to encapsulate most of it from us.