1
votes

I'd like my program to listen to messages in another Windows app.

I am aware of SetWindowsHook, but am concerned about this line:

If the dwThreadId parameter is zero or specifies the identifier of a thread created by a different process, the lpfn parameter must point to a hook procedure in a DLL.

I don't know which hook procedures are in Windows DLLs, so am at a loss.

(I'm using Python with ctypes to use the WinAPI.)

Question: How can I get my program to listen for messages in another Windows process?

1
This is very tricky API. In my view it is unrealistic to attempt to use it without at least a good grounding in winapi and gaining that experience through ctypes is a very suboptimal way to do it. It's far better to do that learning with a language that has more direct access to the winapi, C or C++ for instance. I recommend you do that first. - David Heffernan
The Windows API surface is huge. Microsoft isn't exactly known to provide more than one way to accomplish any given task, to keep it somewhat manageable and maintainable. Observing messages in a different thread is done using hooks. I'm sure there are other, more involved ways to accomplish the same. Though if using hooks poses a road block, going harder is probably the wrong direction. - IInspectable
Hooks aren't impossible to write. It just takes time to learn the API to a sufficient level to get it right. There are no shortcuts. Think of it as though you are learning a foreign language. You might want to speak the language tomorrow, but it's gonna take a bit more time than that before you can. You can learn a bunch of words from the language very quickly, but can string together conversation? - David Heffernan
@DavidHeffernan Part of the challenge in learning a new thing is that it's hard to know what one doesn't know. So far I know about messages, winprocs, and how to send messages to a window. I know about Spy++ and "inspect." I have only a hazy understanding of message queues, threads, and the innards of DLLs. What other topics are important (for hooks) that I've left out completely? Would working through Charles Petzold's "Programming Windows" give me proficiency in hooks? - Asker
"Would working through Charles Petzold's "Programming Windows" give me proficiency in hooks?" No, that won't cover hooks. But I would very much recommend that you do it, because it would give you the foundational knowledge that would allow you to subsequently learn about hooks. - David Heffernan

1 Answers

2
votes

You need to create a DLL and add hook functions to the DLL.

I will use the following code in C++ to create the DLL:

#include <windows.h>
#include <iostream>

HINSTANCE hThisDLL;
HHOOK hMsgHook;

BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    hThisDLL = hinstDLL;
    return TRUE;
}

LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    //do what you want to do
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

extern "C" __declspec(dllexport) BOOL InstallHook()
{
    if (!hMsgHook)
        hMsgHook = SetWindowsHookEx(WH_GETMESSAGE, &GetMsgProc, hThisDLL, 0);
    return (hMsgHook != NULL);
}

extern "C" __declspec(dllexport) VOID UninstallHook()
{
    if (hMsgHook)
    {
        UnhookWindowsHookEx(hMsgHook);
        hMsgHook = NULL;
    }
}

After generating the .dll file, you can load the dynamic library through the LoadLibrary function, and use the GetProcAddress function to get the function address in the dynamic library. Then call the hook function.

Here is the code with C++:

#include <iostream>
#include <windows.h>
typedef BOOL(WINAPI* LPInstallHook)();
typedef VOID(WINAPI* LPUninstallHook)();
int main()
{
    HMODULE dll_handle;
    LPInstallHook installProc;
    LPUninstallHook uninstallProc;
    HHOOK process_hook;
    dll_handle = LoadLibrary(L"test.dll");
    if (dll_handle)
    {
        installProc = (LPInstallHook)GetProcAddress(dll_handle, "InstallHook");
        uninstallProc = (LPUninstallHook)GetProcAddress(dll_handle, "UninstallHook");
    }
    MSG msg;
    installProc();
    while (GetMessageW(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }

    uninstallProc();

    if(dll_handle) FreeLibrary(dll_handle);
}

This is part of the code implemented in python, you can refer to it:

import ctypes
import os
from ctypes import *

user32 = ctypes.windll.user32
kernel32 = ctypes.windll.kernel32

if __name__ == '__main__':
    dllpath = os.path.join(path,"test.dll")
    pDll = ctypes.WinDLL(dllpath)
    pDll.InstallHook()
    msg = ctypes.wintypes.MSG()
    while user32.GetMessageW(ctypes.byref(msg), 0, 0, 0) != 0:
        user32.TranslateMessage(msg)
        user32.DispatchMessageW(msg)

Of course, according to the documentation, you can set dwThreadId as the identifier of other threads. You can refer to the similar thread.