79420447

Date: 2025-02-07 10:00:46
Score: 0.5
Natty:
Report link

I know this is super old but i just ran across this issue myself the other day, and as the other answer suggested, you cant call SetWindowsHookEx() from the DLLMain function or other threads.

BUT, thats because the DLL main function runs in its own thread, separate from the main thread. And thats relevant because apparently it seems that when you call SetWindowsHookEx(), as soon as the thread that you called that from, expires, then it automatically unhooks for some reason (and i couldn't find this documented anywhere on the internet).

So if you call SetWindowsHookEx() from any thread other than the main one of the target process (assuming you can do that) then theres always a possibility that your hook will be undone before the main process itself expires. And in this instance, once the DLLMain function returns, it terminates the thread and consequently removes the hook.

So to fix this i call the hook from a new thread, and then i suspend the thread indefinitely, which should mean the hook only gets removed when the process itself closes.

The fixed version of the code from the question could look something possibly like this

HHOOK hHook = NULL;
LRESULT CALLBACK KeyHit(int code, WPARAM wParam, LPARAM lParam){
    // replace with whatever logic you want to run in the target process when keyboard event is called
    cout << "keyboard event detected.\n";
    return CallNextHookEx(hHook, code, wParam, lParam);
}
void ThreadHookEvents() {
    DWORD thread_id = 0; // set this to the thread id of your main/hwnd thread
    HHOOK hHook = SetWindowsHookExA(WH_KEYBOARD, (HOOKPROC)KeyHit, 0, thread_id);
    // then pause the thread so the hook never expires
    SuspendThread(GetCurrentThread());
}
BOOL APIENTRY DllMain(HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved){
    switch (ul_reason_for_call){
    case DLL_PROCESS_ATTACH:
        CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)ThreadHookEvents, 0, 0, 0);
        break;
    }
    return TRUE;
}

As a bonus incase someone actually reads this, i put together another small function to get the thread ID of a window (not specifically the main one, if multiple) from the process, which you could just plug right into the above script for getting the thread_id

DWORD GetAWindowThreadID() {
    auto EnumWindowsProc = [](HWND hwnd, LPARAM lParam) -> BOOL {
        DWORD windowProcessId;
        DWORD thread_id = GetWindowThreadProcessId(hwnd, &windowProcessId);
        if (windowProcessId == GetCurrentProcessId())
            *(DWORD*)lParam = thread_id;
        return TRUE; 
    };

    DWORD target_thread = 0;
    EnumWindows(EnumWindowsProc, (LPARAM)&target_thread);
    return target_thread;
}
Reasons:
  • Blacklisted phrase (1): this document
  • Long answer (-1):
  • Has code block (-0.5):
  • Low reputation (1):
Posted by: Connorjt