Understanding __event, __hook(), __unhook(), event_source(), event_receiver()

I was auditing a code that had lot of __hook() and __unhook(). So i wanted to understand how this code is working internally. Google search didn’t turn any good information. So i decided to do some test.

In summary, what is happening is, Microsoft compiler inserts some code in both the sender and receiver class. So that whenever the Event gets called in sender class it calls the function in the receiver class. Compiler maintains a list of event handlers for each and every event in the sender class and whenever the event is called in the sender class this in turn calls the each and every event handler. Each and every event handler is stored in the linked list.

event_source()    –> Compiler understand that this class fire the event

event_receiver() –> Compiler understand that this class receives the event

__event                   –> Used to declare a function/event in the sender class . (tagging this as __event)

__hook/__unhook –> Called by the receiver class to hook the events in the sender class.

This is the sample code i written to do the testing.

#include <stdio.h>
#include <windows.h>

[event_source(native)]
class Sender
{
public:
    Sender()
    {
        // Start: Original constructor code
        // End: Original constructor code
    }
    ~Sender()
    {
        // Start: Original destructor code
        // End: Original destructor code
    }

    // Start: __event
    __event void Event1(int value);
    // End: __event
};

[event_receiver(native)]
class Receiver
{
public:
    void Event1(int value)
    {
        // Start: Original Event1 code
        printf("Event1 called");
        // End: Original Event1 code
    }

    void hookEvents(Sender *pSender)
    {
        // Start: Original __hook code
        __hook(&Sender::Event1, pSender, &Receiver::Event1);
        // End: Original __hook code
    }

    void unhookEvents(Sender *pSender)
    {
        // Start: Original __unhook code
       __unhook(&Sender::Event1, pSender, &Receiver::Event1);
        // End: Original __unhook code
    }
};

int main()
{
    // Start: Original main code
    Sender *pSender = new Sender();
    Receiver *pReceiver= new Receiver();
   
    pReceiver->hookEvents(pSender);
    pSender->Event1(255);
    // End: Original main code
}

We have two classes here “Sender” and “Receiver”. “Sender” class has declared an event function named “Event1”. Note that we haven’t written any definition for that function but we have prefixed that declaration with __event. What does it mean is, “Sender” class can send an event named “Event1” to all classes that has registered themselves with “Sender” class. This is what “Receiver” class is doing. Once we created the “Receiver” class, what we do is, we call a __hook in the “Receiver” class with the object of “Sender” class and “Sender” Event function i.e Event1. What we are internally saying is, whenever “Event1” is called in the “Sender” class it should call the callback function of “Receiver” class.

Compiler implements its using the injected code that keep track of “Receiver” object and function to call for each and every event. Event handlers for a event is kept in a linked list. We can see that injected code when we compile the above sample code using option (/Fx).

Compile this code using the following command line option:

cl /Fx /Zi TestEventhandler.cpp

This the generated code:

// Created by Microsoft (R) C/C++ Compiler Version 10.00.30319.01
//
// compiler-generated file created 10/20/11 at 06:08:45
//
// This C++ source file is intended to be a representation of the
// source code injected by the compiler.  It may not compile or
// run exactly as the original source file.
//

//+++ Start Injected Code
[no_injected_text(true)];      // Suppress injected text, it has already been injected
#pragma warning(disable: 4543) // Suppress warnings about skipping injected text
#pragma warning(disable: 4199) // Suppress warnings from attribute providers

#pragma message("\n\nNOTE: This merged source file should be visually inspected for correctness.\n\n")
//— End Injected Code

//+++ Start Injected Code For Attribute ‘event’
#injected_line 21 "TestEventhandler.cpp"
struct __EventingCriticalSectionStub
{
    void Lock()
    {
    }
    void Unlock()
    {
    }
};
#injected_line 21 "TestEventhandler.cpp"
// injected
template <class T>
    struct __eventingGetAddr
{
    typedef void ( T::*pmfn_type) ();
    typedef void ( *pgfn_type) ();
    union U
    {
        void *addr;
        void ( T::*pmfn)();
        void ( *pgfn)();
    };
    static pmfn_type  __getMAddr(void *addr)
    {
        U u;
        u.addr = addr;
        return u.pmfn;
    }
    static void*  __getVAddr(pmfn_type pmfn)
    {
        U u;
        u.pmfn = pmfn;
        return u.addr;
    }
    static pgfn_type  __getSMAddr(void *addr)
    {
        U u;
        u.addr = addr;
        return u.pgfn;
    }
    static void*  __getSVAddr(pgfn_type pgfn)
    {
        U u;
        u.pgfn = pgfn;
        return u.addr;
    }
};
#injected_line 21 "TestEventhandler.cpp"
struct __eventNode
{
    virtual int __isEqual(void*, void*) = 0;
    virtual int __isEqual(void*) = 0;
    __eventNode* next;
};
#injected_line 21 "TestEventhandler.cpp"
struct __eventMainNode
{
    int key;
    __eventNode* root_node;
    __eventMainNode* next_event;
};

//— End Injected Code For Attribute ‘event’
#include <stdio.h>
#include <windows.h>

[event_source(native)]
class Sender
{
public:
    Sender()
    {
    //+++ Start Injected Code For Attribute ‘event’ from Sender::Sender
    #injected_line 21 "TestEventhandler.cpp"
    __eventHandlerList_Sender_Event1 = 0;

    //— End Injected Code For Attribute ‘event’

        // Start: Original constructor code
        // End: Original constructor code
    }
    ~Sender()
    {
        // Start: Original destructor code
        // End: Original destructor code
    }

    // Start: __event
    __event void Event1(int value);
    // End: __event

    //+++ Start Injected Code For Attribute ‘event’
    #injected_line 21 "TestEventhandler.cpp"
    __EventingCriticalSectionStub __EventingCS;
    #injected_line 21 "TestEventhandler.cpp"
    struct __eventNode_Sender_Event1
    {
        virtual void  __invoke(int i1) = 0;
        virtual int __isEqual(void*, void*) = 0;
        virtual int __isEqual(void*) = 0;
        __eventNode_Sender_Event1* next;
    };
    template <class T>                      // Note: Node that keep track of single Event Handler.
        struct __InvokeHandlers_Sender_Event1 : __eventNode_Sender_Event1
    {
        T* p;
        void ( T::*pmfn) (int i1);
        void  __invoke(int i1)
        {
            return (p->*pmfn) (i1);
        }
        int __isEqual(void* p, void* pfn)
        {
            return ((T*) p == this->p) && (__eventingGetAddr<T>::__getMAddr(pfn) == (void ( T::*) ()) pmfn);
        }
        int __isEqual(void* p)
        {
            return ((T*) p == this->p);
        }
    };
    __eventNode_Sender_Event1* __eventHandlerList_Sender_Event1;   // Note: Keep track of Event Handlers using Linked List.
    template <class T>
       long __AddEventHandler_Sender_Event1(T* pThis, void ( T::*pmfn) (int i1))
    {
        __InvokeHandlers_Sender_Event1<T>* newHead = new __InvokeHandlers_Sender_Event1<T>;
        newHead->p = pThis;
        newHead->pmfn = pmfn;
        __EventingCS.Lock();
        __try
        {
            newHead->next = __eventHandlerList_Sender_Event1;
            __eventHandlerList_Sender_Event1 = (__eventNode_Sender_Event1*) newHead;
        }
        __finally
        {
            __EventingCS.Unlock();
        }
        return 0;
    }
    template <class T>
        long __RemoveEventHandler_Sender_Event1(T* pThis, void ( T::*pmfn) (int i1))
    {
        __EventingCS.Lock();
        long retcode = 1;
        __try
        {
            __eventNode_Sender_Event1* node = __eventHandlerList_Sender_Event1;
            __eventNode_Sender_Event1* prev = 0;
            for (; node != 0; node = node->next) {
                if (node->__isEqual(pThis,
                                    __eventingGetAddr<T>::__getVAddr((void (T::*) ()) pmfn)) != 0) {
                    if (prev == 0) {
                        __eventHandlerList_Sender_Event1 = node->next;
                    }
                    else {
                        prev->next = node->next;
                    }
                    delete node;
                    retcode = 0;
                    __leave;
                }
                prev = node;
            }
        }
        __finally
        {
            __EventingCS.Unlock();
        }
        return retcode;
    }
    template <class T>
        void __RemoveAllEventHandlers_Sender_Event1(T* pThis)
    {
        __EventingCS.Lock();
        __try
        {
            __eventNode_Sender_Event1* node = __eventHandlerList_Sender_Event1;
            __eventNode_Sender_Event1* prev = 0;
            while (node != 0) {
                if (node->__isEqual(pThis) != 0) {
                    __eventNode_Sender_Event1* dead = node;
                    if (prev == 0) {
                        __eventHandlerList_Sender_Event1 = node = node->next;
                    }
                    else {
                        prev->next = node = node->next;
                    }
                    delete dead;
                }
                else {
                    prev = node;
                    node = node->next;
                }
            }
        }
        __finally
        {
            __EventingCS.Unlock();
        }
    }
#injected_line 21 "TestEventhandler.cpp"
    void __RemoveAllEventHandlers(void* pThis)
    {
        __RemoveAllEventHandlers_Sender_Event1(pThis);
    }

    //— End Injected Code For Attribute ‘event’
};

//+++ Start Injected Code For Attribute ‘event’
#injected_line 21 "TestEventhandler.cpp"
inline void  Sender::Event1(int i1)                               // Note: Generated Event1 Function.
{
    __EventingCS.Lock();
    __try
    {
        __eventNode_Sender_Event1* node = __eventHandlerList_Sender_Event1;
        for (; node != 0; node = node->next) {
            node->__invoke(i1);
        }
    }
    __finally
    {
        __EventingCS.Unlock();
    }
}

//— End Injected Code For Attribute ‘event’

[event_receiver(native)]
class Receiver
{
public:
    void Event1(int value)
    {
        // Start: Original Event1 code
        printf("Event1 called");
        // End: Original Event1 code
    }

    void hookEvents(Sender *pSender)
    {
        // Start: Original __hook code
        __hook(&Sender::Event1, pSender, &Receiver::Event1)
    //+++ Start Injected Code For Attribute ‘hook’ from Receiver::hookEvents
    #injected_line 39 "TestEventhandler.cpp"
(pSender)->__AddEventHandler_Sender_Event1((Receiver*) this, &Receiver::Event1)  // Note: Adding themself as Event Handler

    //— End Injected Code For Attribute ‘hook’
;
        // End: Original __hook code
    }

    void unhookEvents(Sender *pSender)
    {
        // Start: Original __unhook code
        __unhook(&Sender::Event1, pSender, &Receiver::Event1)
    //+++ Start Injected Code For Attribute ‘unhook’ from Receiver::unhookEvents
    #injected_line 46 "TestEventhandler.cpp"
(pSender)->__RemoveEventHandler_Sender_Event1((Receiver*) this, &Receiver::Event1)   // Note: Removing themself from the list of Event Handler

    //— End Injected Code For Attribute ‘unhook’
;
        // End: Original __unhook code
    }
};

int main()
{
    // Start: Original main code
    Sender *pSender = new Sender();
    Receiver *pReceiver= new Receiver();
   
    pReceiver->hookEvents(pSender);
    pSender->Event1(255);
    // End: Original main code
}

This does not use any OS features. It’s a pure C++ way of implementing Event handling and callback.

Advertisements
This entry was posted in Windows VC++ and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s