SetWindowsHookEx не работает с WH_CBT для другого идентификатора потока процесса (но работает с собственным идентификатором потока)

Я ломал голову над этим в течение нескольких дней, и мне может понадобиться помощь! Я могу обнаружить крючок CBT, используя SetWindowsHookEx, когда он подключен к идентификатору потока моего приложения WPF, но я не могу заставить его подключиться к идентификатору потока другого окна процесса, когда это окно становится приложением переднего плана .

Изображение 1: показывает, что я МОГУ получить хук CBT для обнаружения максимизации окна по идентификатору потока основного приложения Изображение 1: показывает, что я МОГУ получить хук CBT для обнаружения максимального окна в потоке основного приложения.  ID

Изображение 2: показывает, что я НЕ МОГУ получить хук CBT при прослушивании идентификатора потока другого приложения, и это приведет к сбою приложения! Изображение 2: показывает, что я НЕ МОГУ получить крючок CBT при прослушивании другого  идентификатор потока приложения, и это приведет к сбою приложения!

Я хочу избежать отправки ThreadId=0 и создания полного глобального хука, потому что я знаю, что хочу слушать только приложение переднего плана, а не все приложения на рабочем столе. Я хочу иметь возможность прослушивать несколько оконных событий до того, как они произойдут (WH_CBT делает это, насколько я понимаю) для любого окна, которое в настоящее время имеет фокус переднего плана.

Опять же, следующий код работает, когда текущее приложение WPF становится приложением переднего плана, но происходит сбой, когда окно другого приложения становится передним планом (например, Блокнот, Internet Explorer, File Explorer, Chrome и т. д.).

Полный код: (ссылка на zip-файл github)

Вот несколько фрагментов кода, чтобы показать, что я сделал:

DLL, определяющая обратный вызов (inject.dll):

inject.h: Snippet
typedef void(__stdcall* MYFUNCPTR)(int code, WPARAM wparam, LPARAM lparam);
extern "C" __declspec(dllexport) void Init(MYFUNCPTR funcPtr);
extern "C" __declspec(dllexport) LRESULT CALLBACK CbtProcCallback(int code, WPARAM wparam, LPARAM lparam);
WPARAM wparam, LPARAM lparam);
MYFUNCPTR _handler = 0;
inject.cpp: Snippet
void Init(MYFUNCPTR funcPtr)
{
    _handler = funcPtr;
}
LRESULT CALLBACK CbtProcCallback(int code, WPARAM wparam, LPARAM lparam)
{
    if (code >= 0)
    {
        // Only send the code if you are about to MAXIMIZE
        if (code == HCBT_MINMAX)
        {
            if (lparam == SW_MAXIMIZE)
            {
                _handler(0, wparam, lparam);
            }
        }
    }

    return CallNextHookEx(NULL, code, wparam, lparam);
}

DLL, устанавливающая хук CBT (dllwrapper.dll):

dllwrapper.cpp: Snippet
    // Load library in which we'll be hooking our functions.
    HMODULE dll = LoadLibrary(L"inject.dll");
    if (dll == NULL) {
        char errorMessage[100];
        sprintf_s(errorMessage, "ERR-LoadLibrary failed! ErrorCode=%d", GetLastError());
        SendManagedMessage(errorMessage);
        return false;
    }
    SendManagedMessage("LoadLibrary passed!");

    // Get the address of the function inside the DLL.
    MYPROC iaddr = (MYPROC)GetProcAddress(dll, "Init");
    if (iaddr == NULL) {
        char errorMessage[100];
        sprintf_s(errorMessage, "ERR-GetProcAddress for Init failed! ErrorCode=%d", GetLastError());
        SendManagedMessage(errorMessage);
        return false;
    }
    SendManagedMessage("GetProcAddress for Init passed!");
    iaddr(OnInjectionCallback);

    // Get the address of the function inside the DLL.
    HOOKPROC cbtProcAddress = (HOOKPROC)GetProcAddress(dll, "CbtProcCallback");
    if (cbtProcAddress == NULL) {
        char errorMessage[100];
        sprintf_s(errorMessage, "ERR-GetProcAddress for CbtProcCallback failed! ErrorCode=%d", GetLastError());
        SendManagedMessage(errorMessage);
        return false;
    }
    SendManagedMessage("GetProcAddress for CbtProcCallback passed!");

    // Hook the function
    cbtProcHook = SetWindowsHookEx(WH_CBT, cbtProcAddress, dll, _threadId);
    if (cbtProcHook == NULL) {
        char errorMessage[100];
        sprintf_s(errorMessage, "ERR-SetWindowsHookEx cbtProcAddress failed! ErrorCode=%d", GetLastError());
        SendManagedMessage(errorMessage);
        return false;
    }
    SendManagedMessage("SetWindowsHookEx for cbtProcAddress passed!");
Snippet exporting to C#
typedef void(__stdcall* CodeCallback)(int code, WPARAM wparam, LPARAM lparam);
typedef void(__stdcall* MessageCallback)(const char* message);
#ifdef __cplusplus
extern "C" {  // only need to export C interface if
              // used by C++ source code
#endif

    __declspec(dllexport) bool StartHooks(unsigned int threadId, MessageCallback messageCallback, CodeCallback codeCallback);
    __declspec(dllexport) void StopHooks();

#ifdef __cplusplus
}
#endif
NativeMethods.cs: Snippet of C# dll imports to the WPF app
public delegate void MessageCallback(string message);
public delegate void CodeCallback(int code, IntPtr wParam, IntPtr lParam);
[DllImport("dllwrapper.dll")]
public extern static bool StartHooks(uint threadId, MessageCallback messageHandler, CodeCallback codeHandler);
[DllImport("dllwrapper.dll")]
public extern static void StopHooks();

Из сообщений в окне приложения WPF я вижу, что хук проходит и не возвращает никаких ошибок Win32, но он просто не выполняет обратный вызов, когда фокус находится в другом окне (даже при использовании отладчика).

Любая помощь будет принята с благодарностью!

Среда разработки:

  • Виндовс 10 1909
  • VS2019 16.7.4
  • С# .NET Framework 4.7.2, С++

person Tam Bui    schedule 06.10.2020    source источник


Ответы (1)


Когда основное приложение вызывает Init(), оно вызывает свой собственный экземпляр DLL и, таким образом, устанавливает свою собственную копию _handler.

Когда ваша подключаемая DLL внедряется в другой процесс, этот процесс получает свою собственную копию DLL и, следовательно, собственную копию _handler. Но Init() никогда не вызывается для этого экземпляра DLL, поэтому его копия _handler по-прежнему равна 0, когда ваша функция ловушки вызывается внутри этого процесса. Вот почему процесс падает, так как хук не проверяет это условие.

Ваша DLL в другом процессе не может вызывать функцию в вашем основном приложении через границы процесса. Вам придется изменить функцию ловушки, чтобы вместо этого использовать выбранный вами механизм IPC для связи с основным процессом приложения, когда _handler равен 0. Для этой связи могут использоваться оконные сообщения, каналы, почтовые ящики, сокеты и т. д.

person Remy Lebeau    schedule 06.10.2020
comment
Если кому-то интересно, я разместил рабочий образец с предложением IPC Реми. Я использовал общий механизм FileMapping для IPC, который очень легковесен. github.com/Tam66662 /samples/blob/master/stackoverflow/questions/ - person Tam Bui; 07.10.2020