-1

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.

Nur1
  • 418
  • 4
  • 11
  • 1
    all your details is unrelated. if crash - error in your code. but your code you not show – RbMm Jun 19 '21 at 00:46
  • ok gonna provide some code. – Nur1 Jun 19 '21 at 00:47
  • @Nur1 You are hooking a *thread*, so `wmProcCallback()` is going to receive ALL dispatched messages for ALL windows belonging to that thread. Your crash is going to end up being due to a logic error inside of `wmProcCallback()`, but wee can't see that code, so we can't diagnose the error. – Remy Lebeau Jun 19 '21 at 00:53
  • and you must at first debug ! look where crash/exception - no simply say crash. place, callstack, symbols must be – RbMm Jun 19 '21 at 00:53
  • I updated my question with some demo code. Thanks so far. – Nur1 Jun 19 '21 at 02:13
  • No. You updated the question with links to off-site resources. That's not helpful. Please read [ask]. – IInspectable Jun 19 '21 at 08:01
  • `switch (ul_reason_for_call)` have no `break;` as result you call `logfile.close();` just after `logfile.open` and multiple times, after every thread create/exit/dll unload – RbMm Jun 19 '21 at 08:53
  • @RbMm It was missing in the demo I prepared, sry about that, added it! – Nur1 Jun 19 '21 at 09:01
  • [Dynamic-Link Library Best Practices](https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices). Don't forget, you're a guest in someone else's house, so you better play by the rules. – IInspectable Jun 19 '21 at 09:06
  • attach debugger to process, if was unhandled exception - look - where it, call stack, etc – RbMm Jun 19 '21 at 09:18
  • probably crash in `logfile << *` – RbMm Jun 19 '21 at 09:20
  • @RbMm gonna look forward to debug this properly. The crash still occurs even without any logging. – Nur1 Jun 19 '21 at 09:35
  • @IInspectable I read this page and always look back to it. If we took out the logging this will be a tiny dll which should (and do) work. But as in my question, it's all about the hwnd somehow... :( – Nur1 Jun 19 '21 at 09:37
  • If you want to identify the issue, remove the code that is known to be incorrect. Please update the question. – IInspectable Jun 19 '21 at 09:40
  • @IInspectable There is no code that is known to be incorrect. I just said that it still behaves the same even if the logging is not there at all. The last update contains a full example and I adapted the entire question for it. – Nur1 Jun 19 '21 at 10:46
  • The logging **is** there. If you want help with your code, show the code you are using. – IInspectable Jun 19 '21 at 11:25
  • @IInspectable I am using it with logging :) "If you want help with your code, show the code you are using." I literally created a full example here – Nur1 Jun 19 '21 at 12:01
  • Executing C++ code from `DllMain` is guaranteed to be broken. Just remove it. Using global objects in a DLL is broken. Remove it. If you want to solve this all by yourself, be my guest. – IInspectable Jun 19 '21 at 12:03
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/233956/discussion-between-nur1-and-iinspectable). – Nur1 Jun 19 '21 at 12:04
  • What you really need to do is debug the crash in explorer. All we can do without that is guess. (For example, your code takes action when `nCode > HC_ACTION` even though it should take action only when `nCode == HC_ACTION`. Is that the problem? We need the explorer crash stack to know for sure.) – Raymond Chen Jun 19 '21 at 12:57
  • @RaymondChen At this point, I am not sure if this is a crash. The window just closes. And if several explorer windows are open only the hooked one is closed. I did debug through, the dll seems ok and the wmProcCallback function is also executed when receiving messages. but the closure happens after. – Nur1 Jun 19 '21 at 15:27
  • Are you trying to inject a 32-bit hook into a 64-bit process? [The documentation](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowshookexa) calls out that in that case, the thread which sets the hook must pump messages, but yours doesn't. – Raymond Chen Jun 19 '21 at 15:40
  • @RaymondChen Both explorer.exe and my demo code runs on x64. I am aware of that. – Nur1 Jun 19 '21 at 17:17

1 Answers1

0

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

Nur1
  • 418
  • 4
  • 11