c++winapievent-hooking

WinEventHook does not catch events from specific process id


I'm creating a console process and then trying to monitor it for events with SetWinEventHook. If I use the PID associated with the process I've created, I never catch any events. If I set pid/thread to 0/0 (all processes/threads) then I get plenty of results. It seems like something is wrong in the way I'm trying to hook to a specific process. Any ideas would be appreciated.

#include "stdafx.h"
#include <windows.h>
#include <Oleacc.h>


DWORD CreateChildProcess();
void InitializeMSAA(DWORD pid);
void ShutdownMSAA();
void SetConsoleBufferSize(DWORD processId, short columns, short rows);
void CALLBACK HandleWinEvent(HWINEVENTHOOK hook, DWORD event, HWND hwnd,
    LONG idObject, LONG idChild,
    DWORD dwEventThread, DWORD dwmsEventTime);

HWINEVENTHOOK g_hook;

int main()
{
    DWORD pid = CreateChildProcess();
    Sleep(5000);
    InitializeMSAA(pid);
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

// Initializes COM and sets up the event hook.
void InitializeMSAA(DWORD pid)
{
    // Initializes Component Object Model library
    CoInitialize(NULL);

    g_hook = SetWinEventHook(
        EVENT_MIN, EVENT_MAX,       // Range of events (Console).
        NULL,                                          // Handle to DLL.
        HandleWinEvent,                                // The callback.
        pid, 0,              // Process and thread IDs of interest (0 = all)
        WINEVENT_OUTOFCONTEXT); // Flags.

    //| WINEVENT_SKIPOWNPROCESS
}

// Unhooks the event and shuts down COM.
void ShutdownMSAA()
{
    UnhookWinEvent(g_hook);
    CoUninitialize();
}

// Callback function that handles events.
void CALLBACK HandleWinEvent(HWINEVENTHOOK hook, DWORD event, HWND hwnd,
    LONG idObject, LONG idChild,
    DWORD dwEventThread, DWORD dwmsEventTime)
{
    IAccessible * pAcc = NULL;
    VARIANT varChild;
    HRESULT hr = AccessibleObjectFromEvent(hwnd, idObject, idChild, &pAcc, &varChild);
    if ((hr == S_OK) && (pAcc != NULL))
    {
        BSTR bstrName;
        pAcc->get_accName(varChild, &bstrName);
        if (event == EVENT_SYSTEM_MENUSTART)
        {
            printf("Begin: ");
        }
        else if (event == EVENT_SYSTEM_MENUEND)
        {
            printf("End:   ");
        }
        printf("%S\n", bstrName);
        SysFreeString(bstrName);
        pAcc->Release();
    }
}

// Creates a bash child process with i/o to a given console screen buffer
DWORD CreateChildProcess()
{
    // In order to launch bash in System32, program must be built as x64
    LPCTSTR applicationAddress = L"C:\\Windows\\System32\\bash.exe";

    PROCESS_INFORMATION piProcInfo;
    STARTUPINFO siStartInfo;

    // Set up members of the PROCESS_INFORMATION structure.
    SecureZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));

    // Set up members of the STARTUPINFO structure.
    // This structure specifies the STDIN and STDOUT handles for redirection.
    SecureZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
    siStartInfo.cb = sizeof(STARTUPINFO);

    // NB: Initial window size settings don't work for some reason.
    auto minX = GetSystemMetrics(SM_CXMIN);
    auto minY = GetSystemMetrics(SM_CYMIN);
    siStartInfo.dwXSize = 200;
    siStartInfo.dwYSize = 200;
    //siStartInfo.dwXCountChars = 119;
    //siStartInfo.dwYCountChars = 9;

    //siStartInfo.wShowWindow = SW_HIDE;
    siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESIZE;
    // | STARTF_USESHOWWINDOW | STARTF_USECOUNTCHARS 

    // Create the child process. 
    BOOL success = CreateProcess(
        applicationAddress, // absolute path to the application
        TEXT("-i"),         // command line 
        NULL,               // process security attributes 
        NULL,               // primary thread security attributes 
        TRUE,               // handles are inherited 
        CREATE_NEW_CONSOLE,               // creation flags 
        NULL,               // use parent's environment 
        NULL,               // use parent's current directory 
        &siStartInfo,       // STARTUPINFO pointer 
        &piProcInfo);       // receives PROCESS_INFORMATION 


    if (!success)
    {
        int lastError = GetLastError();
    }

    return piProcInfo.dwProcessId;
}

Solution

  • If I use the PID associated with the process I've created, I never catch any events.

    The reason for that propably is that a console process does not own the console window. The console window is associated with conhost.exe (csrss.exe in versions of Windows prior to Win 7). So the messages related to the console window will be processed by conhost.exe, not the process you created.

    If I set pid/thread to 0/0 (all processes/threads) then I get plenty of results.

    Try to set the PID to that of the conhost.exe process associated with the console application you launched. You should now receive the events only from the console window.

    I don't think there is a direct API to find the associated conhost.exe process but you could try to enumerate all child processes of the new process until you have found "conhost.exe". You propably have to do this in a loop as conhost.exe won't be there immediately after CreateProcess() returned.