Step 3: Respond to Event Notifications

[This is preliminary documentation and subject to change.]

Once a WinEvent hook is successfully in place, your hook procedure will begin receiving event notifications. The following function, taken from the Babble sample application, shows an example of how a hook procedure can receive event notifications and process them.

void CALLBACK WinEventProc(HWINEVENTHOOK hEvent, DWORD event,
    HWND hwndMsg, LONG idObject, LONG idChild, DWORD idThread, DWORD dwmsEventTime)
{
    // What type of event is coming through?
    switch (event) {
        case EVENT_OBJECT_FOCUS:
            // Focus is changing.
            OnFocusChangedEvent(event, hwndMsg, 
                                idObject, idChild,
                                dwmsEventTime);
            break;
        }
    return;
}
 

For simplicity, Babble's WinEventProc function responds only to EVENT_OBJECT_FOCUS events, calling another application-defined function to process them (your client application would probably respond to far more events). When the hook procedure receives an EVENT_OBJECT_FOCUS event, Babble calls the following OnFocusChangedEvent application-defined function to retrieve an interface pointer based on the information Babble received with the notification.

BOOL OnFocusChangedEvent(DWORD event, HWND hwnd,
    LONG idObject, LONG idChild, DWORD dwmsTimeStamp)
{
    VARIANT      varChild;
    IAccessible* pIAcc;
    HRESULT      hr;
    int          cchName = 128;
    TCHAR        tszName[128];
    TCHAR        tszSpeak[1024];
    
    // Important to init variants
    VariantInit(&varChild);

    hr = AccessibleObjectFromEvent(hwnd, (DWORD)idObject, (DWORD)idChild, &pIAcc, &varChild);
    
    // Check to see if we got a valid pointer
    if (SUCCEEDED(hr)) 
    {
        OBJINFO objCurrent;

        objCurrent.hwnd = hwnd;
        objCurrent.plObj = (long*)pIAcc;
        objCurrent.varChild = varChild;

        GetObjectName(&objCurrent, tszName, cchName);

        wsprintf(tszSpeak, "Focus now %s.", tszName);
        
        SpeakString(tszSpeak);

        if (pIAcc)
             pIAcc->Release();

        return(TRUE);
     }
    
     // No object was retrieved, so send a failure value.
    return(FALSE);
}
 

OnFocusChangedEvent uses the AccessibleObjectFromEvent function to retrieve information about the object that generated the event. Note that AccessibleObjectFromEvent doesn't accept an event value. At first this might seem odd, but Babble already knows about what event happened; now it needs information about who generated it. Knowing this, it makes sense that AccessibleObjectFromEvent only needs information about the window, object, and child element associated with the event. Also note that Babble initializes the VARIANT structure it sends with the function call to comply with OLE Automation standards.

When AccessibleObjectFromEvent returns, Babble tests the return value with the SUCCEEDED macro. SUCCEEDED is a macro found in the COM interface. If the call succeeded, it uses Microsoft Speech SDK API elements to vocalize the event, then releases the retrieved interface by calling its IUnknown::Release function. This step is required to meet COM standards.