使用钩子向 explorer.exe 主窗口发送消息会导致崩溃?

问题描述

我正在尝试使用从 WH_CALLWNDPROC 获得的 HWND 在窗口上设置 WindowFromPoint 挂钩,然后发送消息 WM_USER+x。但是根据 HWND 我使用它会导致窗口崩溃。

让我解释一下这个场景:

当通过 HWND 函数获得 WindowFromPoint 时,您有时会获得其中一个子窗口而不是主窗口。

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

HWND's

所以我只是得到了窗口

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。

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 是孩子 HWND 时,我钩住,发送消息 -> 工作正常

但是当 targetHwnd 是上面的 HWND(绿色标记的)时,我钩住它,发送消息 -> 它崩溃了。

enter image description here

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

这个完整的演示代码将等待 1 秒钟,然后再拿起 HWND。通过将鼠标悬停在 explorer.exe 窗口的标题栏上,您可以获得导致崩溃的 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); //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,targetTid);
}

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

enter image description here

所以我总共收到了 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 :)