使用挂钩向 explorer.exe main window 发送消息会导致崩溃?

Sending message to explorer.exe main window with a hook causes crash?

我正在尝试使用从 WindowFromPoint 获得的 HWND 在 window 上设置一个 WH_CALLWNDPROC 挂钩,然后发送消息 WM_USER+x。但是根据 HWND 我使用它会使 window.

崩溃

让我解释一下场景:

当通过 WindowFromPoint 函数获得 HWND 时,您有时会获得 children 之一,而不是主要的 window。

使用 spy++ 中的 Finder 工具可视化非常好

所以我简单地得到 window

wcout << "INFO: Waiting 1 second before first hwnd...\n";
this_thread::sleep_for(chrono::milliseconds(1000));
HWND targetHwnd = getHwndFromMousePos(); //Gets hwnd from mouse pos with WindowFromPoint
outHwndData(targetHwnd);
DWORD targetPID;
DWORD targetTID = GetWindowThreadProcessId(targetHwnd, &targetPID);

然后使用dll的导出函数设置钩子

hook = setHook(targetTID); //hook is the global HHOOK and setHook is a exported function in a dll
if (hook == NULL) {
    cout << "ERROR: Could not set hook\n";
    return 1;
}

dll里面的函数是这样的

extern "C" __declspec(dllexport) HHOOK __stdcall setHook(DWORD targetTid) {
    return SetWindowsHookEx(WH_CALLWNDPROC, wmProcCallback, hInst, targetTid);
}

targetHwnd 是 children HWND 时,我挂机,发送消息 -> 工作正常

但是当targetHwnd是上面的HWND(绿色标记的那个)的时候,我挂了它,发了一条消息->它崩溃了。

所以最后 SetWindowsHookEx 有效,但我无法向找到的 window 发送任何消息,如果它是上层 HWND(绿色标记的一个)。为什么会这样?

这个完整的演示代码将等待 1 秒钟,然后再选择 HWND。通过将鼠标悬停在 explorer.exe window 上的标题栏上,您可以获得导致崩溃的 HWND

申请代码:

//This is the app
#include <Windows.h>
#include <iostream>
#include <thread>

using namespace std;

typedef HHOOK(WINAPI* DLLFUNC_SETHOOK) (DWORD);

HINSTANCE dllInstance;
DLLFUNC_SETHOOK setHook;

HHOOK hook;

HWND getHwndFromMousePos() {
    POINT cursorPos;
    if (GetCursorPos(&cursorPos) == FALSE) {
        cout << "ERROR: Could not get Cursor position...\n";
        return NULL;
    }
    HWND wnd = WindowFromPoint(cursorPos);
    if (wnd == NULL) {
        cout << "ERROR: No window found on this point\n";
        return NULL;
    }
    return wnd;
}


int main() {

    wcout << "INFO: Waiting 1 second before first hwnd...\n";
    this_thread::sleep_for(chrono::milliseconds(1000));
    HWND targetHwnd = getHwndFromMousePos();

    wcout << targetHwnd << endl;
    DWORD targetPID;
    DWORD targetTID = GetWindowThreadProcessId(targetHwnd, &targetPID);

    dllInstance = LoadLibrary(L"DLL1.dll");
    setHook = (DLLFUNC_SETHOOK)GetProcAddress(dllInstance, "setHook");
    if (dllInstance == NULL) {
        cout << "ERROR: dllInstance is NULL\n";
        return 1;
    }
    if (setHook == NULL) {
        cout << "ERROR: setHook function is NULL\n";
        return 1;
    }

    hook = setHook(targetTID);
    if (hook == NULL) {
        cout << "ERROR: Could not set hook\n";
        return 1;
    }

    cout << "INFO: Hooked successfully\n";

    //Works only when targetHwnd is a children
    SendMessage(targetHwnd, WM_USER + 1, 0, 0); //This causes the crash

    cin.ignore();

    BOOL success = UnhookWindowsHookEx(hook);
    if (success == FALSE) {
        cout << "ERROR: Could not unhook\n";
    }
    else {
        cout << "INFO: Unhooked hook. Exiting...\n";
    }
}

DLL(DLL1):

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <fstream>

extern HINSTANCE hInst;
extern std::wofstream logfile;
extern "C" __declspec(dllexport) HHOOK __stdcall setHook(DWORD targetTid);

HINSTANCE hInst;
std::wofstream logfile;

BOOL APIENTRY DllMain( HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    hInst = hModule;
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH: { 
            logfile.open("D:\projects\crashDemo\log.txt"); //run "Get-Content -Path "log.txt" -Wait" in Powershell for realtime logs
            break;
        }
        case DLL_THREAD_ATTACH: break;
        case DLL_THREAD_DETACH: break;
        case DLL_PROCESS_DETACH: {
            logfile.close();
            break;
        }
    }
    return TRUE;
}

LRESULT CALLBACK wmProcCallback(int nCode, WPARAM wParam, LPARAM lParam) {
    if (nCode >= HC_ACTION) {
        PCWPSTRUCT cwpStruct = (PCWPSTRUCT)lParam;
        switch (cwpStruct->message) {
        case WM_USER + 1:
            logfile << "WM_USER+1 Message received" << std::endl;
            break;
        }
    }
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

extern "C" __declspec(dllexport) HHOOK __stdcall setHook(DWORD targetTid) {
    return SetWindowsHookEx(WH_CALLWNDPROC, wmProcCallback, hInst, targetTid);
}

编辑:我调试通过了。看起来 dll 一切正常。它加载并且 wmProcCallback 被调用了几次。 我的 WM_USER+1 也被识别,但在处理完 wmProcCallback 中的所有消息后,它会立即关闭 window 并留下以下错误消息。

所以我总共收到了 4 条错误消息:

  1. &2.An outgoing call cannot be made since the application is dispatching an input-asynchronous call

  2. The operation is not permitted because the calling application is not the owner of the data on the clipboard.

  3. Unspecified error

是否有人知道其中哪些是相关的,因为我不知道这些错误中的任何一个?现在要做一些研究。如果有人可以帮助解决这个问题,那就太好了。

简单修复,不要使用 WM_USER+1 :)