0

I'm trying to detect all key presses and mouse events by registering WH_KEYBOARD_LL and WH_MOUSE_LL hooks. Apparently these low-level hooks don't require the hook procedure to reside in a separate DLL.

I have this working in the following example app (a console application).

#include <iostream>
#include <Windows.h>
#include <Winuser.h>
#include <thread>
#include <chrono>
#include <sstream>
#include <atomic>
#include <cassert>

LRESULT CALLBACK wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  std::cout << std::endl << "EVENT: " << hWnd << ", " << message << ", " << wParam << ", " << lParam << std::endl;
  return 0;
}

LRESULT CALLBACK keyboardHook(
  _In_ int    nCode,
  _In_ WPARAM wParam,
  _In_ LPARAM lParam
)
{
  std::cout << "Keyboard: " << nCode << ", " << wParam << ", " << lParam << std::endl;
  return CallNextHookEx(NULL, nCode, wParam, lParam);
}

LRESULT CALLBACK mouseHook(
  _In_ int    nCode,
  _In_ WPARAM wParam,
  _In_ LPARAM lParam
)
{
  std::cout << "Mouse: " << nCode << ", " << wParam << ", " << lParam << std::endl;
  return CallNextHookEx(NULL, nCode, wParam, lParam);
}

int main()
{
  const wchar_t pszClassName[] = L"MyMessageWindow";
  auto hInstance = GetModuleHandle(NULL);

  WNDCLASSEXW wcl;
  ZeroMemory(&wcl, sizeof(WNDCLASSEXW));

  wcl.cbSize = sizeof(WNDCLASSEXW);
  wcl.hInstance = hInstance;
  wcl.lpszClassName = pszClassName;
  wcl.lpfnWndProc = wndProc;

  assert(RegisterClassExW(&wcl) != 0);

  auto hwnd = CreateWindowW(
    pszClassName,
    pszClassName,
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    HWND_MESSAGE,
    NULL,
    NULL,
    NULL
  );

  auto threadId = 0;

  std::thread t([hwnd, threadId]() {
    auto keyboardHookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, keyboardHook, NULL, threadId);
    auto mouseHookHandle = SetWindowsHookEx(WH_MOUSE_LL, mouseHook, NULL, threadId);

    while (true) {
      MSG msg;
      GetMessage(&msg, hwnd, 0, 0);
    }
  });

  t.join();

  return 0;
}

So this works, and I can see the keyboard and mouse events printed to the console regardless of what apps are in focus (if any), which is great.

However, when I do this inside a UMDF driver, the messages don't come through. The calls to SetWindowsHookEx appear to succeed without error.

I'm thinking of creating a minimal UMDF driver to test if it's possible, but thought I'd first ask here in case someone can tell me whether it's possible. If not, I have a few other approaches in mind.

Thanks

Caius Cosades
  • 33
  • 1
  • 9
  • 1
    I strongly doubt that the (incomplete) code we see works as expected. It filters window messages by `HWND` on a thread that doesn't own that window. – IInspectable Sep 01 '20 at 09:49
  • @IInspectable I've updated the code sample to contain a complete example. The call to GetMessage is blocking, hence in a different thread. The documentation for SetWindowsHookEx states (regarding the dwThreadId param): "For desktop apps, if this parameter is zero, the hook procedure is associated with all existing threads running in the same desktop as the calling thread.". I notice now, it says "Desktop app". Maybe that's the problem. I can't get it to work with any number other than 0. – Caius Cosades Sep 01 '20 at 10:31
  • @CaiusCosades The thread that calls `SetWindowsHookEx()` needs a message loop, yes. But that loop should not be filtering messages on an `HWND` at all. Change `while (true) { MSG msg; GetMessage(&msg, hwnd, 0, 0); }` to `MSG msg; while (GetMessage(&msg, NULL, 0, 0)) {}` And don't forget to add a way to break the loop when the thread needs to terminate (suh as posting a `WM_QUIT` message to it), and the thread needs to call `UnhookWindowsHookEx()` when the loop is finished. – Remy Lebeau Sep 01 '20 at 16:47
  • @CaiusCosades And FYI, calling `join()` on a thread immediately after starting the thread defeats the whole purpose of using a thread at all. You may as well get rid of the thread and move the hooks and message loop directly into `main()` instead. – Remy Lebeau Sep 01 '20 at 16:50
  • @RemyLebeau This is just an example. This isn't the actual application. Yes, I know about threading thank you. I'm not a complete idiot. – Caius Cosades Sep 01 '20 at 17:13
  • @CaiusCosades I didn't imply that you were – Remy Lebeau Sep 01 '20 at 17:15

0 Answers0