c++winapihookhwnd

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


I am trying to set a WH_CALLWNDPROC hook on a window using the HWND I got from WindowFromPoint and send a message WM_USER+x after. But depending on the HWND I use it crashes the window.

Let me explain the scenario:

When getting an HWND through the WindowFromPoint function you sometimes get one of the children's instead of the main window.

Pretty good visualized with the Finder tool inside spy++

HWND's

So I simply get the 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);

then set the hook by using the exported function of the 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;
}

The function inside the dll looks like so

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

When targetHwnd is a children HWND, I hook, send message -> works fine

But when targetHwnd is the upper HWND (green marked one), I hook it, send a message -> it crashes.

enter image description here

So at the end SetWindowsHookEx works, but I am unable to send any messages to that found window if it is the upper HWND (green marked one). Why is that so?

This full demo code will wait 1 second before picking up the HWND. By hovering the mouse over the title bar on an explorer.exe window you can get the HWND that causes the crash.

Application code:

//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";
    }
}

The 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);
}

EDIT: I did debug through. It looks like everything is ok with the dll. It loads and the wmProcCallback is called several times as it should. Also my WM_USER+1 is recognized but after handling all messages in the wmProcCallback it closes the window immediately and leaves the following error message.

enter image description here

So I got 4 error messages in total:

  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

Does someone know which of them relevant because I don't know any of these errors? Gonna do some research now. Would be nice if someone can help out on this.


Solution

  • Simple fix, don't use WM_USER+1 :)