#include <iostream>
#include <windows.h>
LRESULT CALLBACK mouseProc(int nCode, WPARAM wParam, LPARAM lParam){
if(wParam == WM_MOUSEWHEEL){
std::cout << "wheel: " << GET_WHEEL_DELTA_WPARAM(wParam) << std::endl;
}else{
MOUSEHOOKSTRUCT* mouselparam = (MOUSEHOOKSTRUCT*)lParam;
std::cout << "etc: " << wParam << " - " << mouselparam->pt.x << "x" << mouselparam->pt.y << std::endl;
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
int main(int argc, char *argv[]) {
HHOOK hhkLowLevelMouse = SetWindowsHookEx(WH_MOUSE_LL, mouseProc, 0, 0);
MSG msg;
while (!GetMessage(&msg, NULL, NULL, NULL)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnhookWindowsHookEx(hhkLowLevelMouse);
return 0;
}
This is the full code.
"etc:" works perfectly as I intended.
"wheel:" always outputs 0.
am i missing something?
Using HIWORD instead of GET_WHEEL_DELTA_WPARAM gives the same result.
GET_WHEEL_DELTA_WPARAM() only works with the wParam of a real WM_MOUSEWHEEL window message, not the wParam of a WH_MOUSE_LL hook.
In the hook, the wParam is just the message identifier by itself, nothing more. ALL of the mouse details are stored in a MSLLHOOKSTRUCT struct pointed by the lParam. Which you attempted to look at for all mouse messages other than WM_MOUSEWHEEL, but you are looking at the wrong struct (MOUSEHOOKSTRUCT is used by WH_MOUSE, not WH_MOUSE_LL).
Per the LowLevelMouseProc callback function documentation:
wParam [in]
Type: WPARAM
The identifier of the mouse message. This parameter can be one of the following messages: WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_MOUSEHWHEEL, WM_RBUTTONDOWN, or WM_RBUTTONUP.
lParam [in]
Type: LPARAM
A pointer to an MSLLHOOKSTRUCT structure.
And the MSLLHOOKSTRUCT structure documentation:
mouseData
Type: DWORD
If the message is WM_MOUSEWHEEL, the high-order word of this member is the wheel delta. The low-order word is reserved. A positive value indicates that the wheel was rotated forward, away from the user; a negative value indicates that the wheel was rotated backward, toward the user. One wheel click is defined as WHEEL_DELTA, which is 120.
Try this instead:
LRESULT CALLBACK mouseProc(int nCode, WPARAM wParam, LPARAM lParam){
if (nCode == HC_ACTION) {
MSLLHOOKSTRUCT* mouselparam = (MSLLHOOKSTRUCT*)lParam;
if (wParam == WM_MOUSEWHEEL) {
short delta = HIWORD(mouselparam->mouseData);
// alternatively, GET_WHEEL_DELTA_WPARAM() would also work here:
// short delta = GET_WHEEL_DELTA_WPARAM(mouselparam->mouseData);
std::cout << "wheel: " << delta << std::endl;
}else{
std::cout << "etc: " << wParam << " - " << mouselparam->pt.x << "x" << mouselparam->pt.y << std::endl;
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
Related
How do i 'block' the WM_LBUTTONDOWN message to be fired?
The function is inside of a dll, I also tried to use LowLevelMouseProc but it does not work with error code: 1429 which means "global only hook".
I don't own the window in question.
I tried to return a WM_NULL in the code below, but it also doesn't work, what else i could try?
extern "C" __declspec(dllexport) LRESULT CALLBACK mouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
MSLLHOOKSTRUCT* mhs = nullptr;
int x = 0;
int y = 0;
std::wstringstream ss;
std::wstringstream ss2;
if (nCode == HC_ACTION)
{
switch (wParam)
{
case WM_LBUTTONDOWN:
ss << L"\nWM_LBUTTONDOWN " << wParam;
wParam = WM_NULL;
break;
case WM_LBUTTONUP:
ss << L"\nWM_LBUTTONUP " << wParam;
break;
case WM_MOUSEMOVE:
break;
case WM_RBUTTONDOWN:
ss << L"\nWM_RBUTTONDOWN: " << wParam;
break;
case WM_RBUTTONUP:
ss << L"\nWM_RBUTTONUP: " << wParam;
break;
default:
ss << L"\nUnknown msg: " << wParam;
break;
}
}
OutputDebugString(ss.str().c_str());
mhs = reinterpret_cast<MSLLHOOKSTRUCT*>(lParam);
x = mhs->pt.x;
y = mhs->pt.y;
ss2 << L"\nx: " << x << L" y: " << y;
OutputDebugString(ss2.str().c_str());
return CallNextHookEx(NULL, nCode, wParam, lParam);
Per the MouseProc callback function documentation:
If nCode is greater than or equal to zero, and the hook procedure did not process the message, it is highly recommended that you call CallNextHookEx and return the value it returns; otherwise, other applications that have installed WH_MOUSE hooks will not receive hook notifications and may behave incorrectly as a result. If the hook procedure processed the message, it may return a nonzero value to prevent the system from passing the message to the target window procedure.
As for your WH_MOUSE_LL error, you can't install that hook on a thread-specific basis, only globally.
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
I am trying to use Clipboard Format Listener for my C++ Console Application. The goal is to monitor every change in the clipboard. I create MessageOnly window, successfully call AddClipboardFormatListener in WM_CREATE, but never get WM_CLIPBOARDUPDATE message in WindowProc function.
#include <iostream>
#include "windows.h"
using namespace std;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
if (AddClipboardFormatListener(hwnd))
cout << " Listener started" << endl;
else
cout << " Start listener failed" << endl;
break;
case WM_DESTROY:
if (RemoveClipboardFormatListener(hwnd))
cout << " Listener stopped" << endl;
else
cout << " Stop listener failed" << endl;
break;
case WM_CLIPBOARDUPDATE:
// Clipboard content has changed
cout << " Clipboard updated" << endl;
break;
default:
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int main(int argc, char* argv[])
{
HWND hWindow = nullptr;
static const wchar_t* className = L"ClipboardListener";
WNDCLASSEX wx = {};
wx.cbSize = sizeof(WNDCLASSEX);
wx.lpfnWndProc = WindowProc;
wx.hInstance = GetModuleHandle(NULL);
wx.lpszClassName = className;
if (!RegisterClassEx(&wx)) {
cout << "Cannot register class" << endl;
}
else
{
hWindow = CreateWindowEx(
0,
className,
L"ClipboardListener",
0, 0, 0, 0, 0,
HWND_MESSAGE,
NULL, NULL, NULL);
}
if (!hWindow)
{
cout << "Cannot create window" << endl;
}
else
{
while (true)
{
// Peek for a WM_CLIPBOARDUPDATE message
MSG message = { 0 };
PeekMessage(&message, hWindow, WM_CLIPBOARDUPDATE, WM_CLIPBOARDUPDATE, PM_REMOVE);
if (message.message == WM_CLIPBOARDUPDATE)
{
cout << "Sample window received WM_CLIPBOARDUPDATE message" << endl;
}
}
}
cin.get();
DestroyWindow(hWindow);
return 0;
}
PeekMessage works well, but I don't want to use loop to receive messages.
If I delete PeekMessage or replace PM_REMOVE with PM_NOREMOVE, nothing changes.
Your message loop is wrong.
CreateWindowEx() sends a WM_CREATE message before exiting, which is why your WindowProc() receives that message.
However, PeekMessage() does not dispatch messages to windows, which is why your WindowProc() does not receive the WM_CLIPBOARDUPDATE messages. Your message loop needs to call DispatchMessage() for that. You should also be using GetMessage() instead of PeekMessage(), so that the loop makes the calling thread sleep when there are no messages to process.
A standard message loop looks more like this:
MSG message;
while (GetMessage(&message, NULL, 0, 0))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
I'm using SetWindowsHookExA(WH_KEYBOARD_LL, HookCallback, GetModuleHandleA(NULL), 0); to set a global hook for capturing the keystrokes, but the result is strange.
The callback function can be executed when I press the "special" keys such as "Enter", "Tab", "Shift", "Ctrl" and other keys having a Virtual Key Code, while it fails to capture the keystrokes when I press the regular letters and digits.
I am confused about it and could anyone tell me the reason?
#include <Windows.h>
#include <iostream>
using namespace std;
HHOOK keyboardHook = 0;
LRESULT CALLBACK HookCallback(int code, WPARAM wParam, LPARAM lParam)
{
KBDLLHOOKSTRUCT *ks = (KBDLLHOOKSTRUCT*)lParam;
cout<< "[TEST] " << ks->vkCode << endl;
return CallNextHookEx(0, code, wParam, lParam);
}
int main()
{
keyboardHook = SetWindowsHookExA(WH_KEYBOARD_LL, HookCallback, GetModuleHandleA(NULL), 0);
if (keyboardHook == 0)
{
cout << "failed" << endl;
return -1;
}
cout << "ok" << endl;
MSG msg;
while(GetMessage(&msg, NULL, 0, 0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnhookWindowsHookEx(keyboardHook);
return 0;
}
I have recently been experimenting with a little project during my limited free time to try and gain more experience and understanding with C++, but I've come to a roadblock in my current program:
I'm trying to create a global low-level mouse listener by using a windows hook, which most things seem fairly straight forward. However, identifying which X mouse button was clicked (MB4 or MB5) and which direction the scroll wheel was rolled is giving me a whole lot of headache.
According to the Microsoft docs, the current way I am trying to identify the appropriate X button clicked and scroll wheel direction is correct, but my implementation of it is not working.
I have been able to find one working solution to the X button issue (the last code segment post in this forum thread), but it seems a bit like jumping through unnecessary hoops when the Microsoft code segment is cleaner and should work.
Though C++ is not my most familiar language, I would like to continue to learn it and use it more often. I hope I'm just making a simple mistake, as this is the first time I have been working with Windows hooks. Thank you in advance for any advice or assistance anyone may be able to offer!
#include <iostream>
#include <windows.h>
static LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if(nCode >= 0)
{
switch(wParam)
{
case WM_LBUTTONDOWN:
system("CLS");
std::cout << "left mouse button down\n";
break;
case WM_LBUTTONUP:
std::cout << "left mouse button up\n";
break;
case WM_RBUTTONDOWN:
system("CLS");
std::cout << "right mouse button down\n";
break;
case WM_RBUTTONUP:
std::cout << "right mouse button up\n";
break;
case WM_MBUTTONDOWN:
system("CLS");
std::cout << "middle mouse button down\n";
break;
case WM_MBUTTONUP:
std::cout << "middle mouse button up\n";
break;
case WM_MOUSEWHEEL:
if(GET_WHEEL_DELTA_WPARAM(wParam) > 0)
std::cout << "mouse wheel scrolled up\n";
else if(GET_WHEEL_DELTA_WPARAM(wParam) < 0)
std::cout << "mouse wheel scrolled down\n";
else //always goes here
std::cout << "unknown mouse wheel scroll direction\n";
break;
case WM_XBUTTONDOWN:
system("CLS");
if(GET_XBUTTON_WPARAM(wParam) == XBUTTON1)
std::cout << "X1 mouse button down\n";
else if(GET_XBUTTON_WPARAM(wParam) == XBUTTON2)
std::cout << "X2 mouse button down\n";
else //always goes here
std::cout << "unknown X mouse button down\n";
break;
case WM_XBUTTONUP:
if(GET_XBUTTON_WPARAM(wParam) == XBUTTON1)
std::cout << "X1 mouse button up\n";
else if(GET_XBUTTON_WPARAM(wParam) == XBUTTON2)
std::cout << "X2 mouse button up\n";
else //always goes here
std::cout << "unknown X mouse button up\n";
break;
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
int main()
{
HHOOK mouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProc, NULL, 0);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnhookWindowsHookEx(mouseHook);
return 0;
}
Please read the documentation:
LowLevelMouseProc callback function:
[...]
wParam [in] Type: WPARAM The identifier of the mouse
message. This parameter can be one of the following messages:
WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL,
WM_MOUSEHWHEEL, WM_RBUTTONDOWN, or WM_RBUTTONUP. lParam
[in] Type: LPARAM A pointer to an MSLLHOOKSTRUCT
structure.
So wParam can be WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_MOUSEHWHEEL, WM_RBUTTONDOWN, or WM_RBUTTONUP. There is no magic way to get any more information out of it. And if there were it would be undocumented and should be avoided.
lParam however points to a MSLLHOOKSTRUCT:
tagMSLLHOOKSTRUCT structure:
Contains information about a low-level mouse input event.
typedef struct tagMSLLHOOKSTRUCT {
POINT pt;
DWORD mouseData;
DWORD flags;
DWORD time;
ULONG_PTR dwExtraInfo;
} MSLLHOOKSTRUCT, *LPMSLLHOOKSTRUCT, *PMSLLHOOKSTRUCT;
[...]
mouseData Type: DWORD
If the message is WM_MOUSEWHEEL, the high-order word of this member is
the wheel delta. The low-order word is reserved. A positive value
indicates that the wheel was rotated forward, away from the user; a
negative value indicates that the wheel was rotated backward, toward
the user. One wheel click is defined as WHEEL_DELTA, which is 120.
If the message is WM_XBUTTONDOWN, WM_XBUTTONUP,
WM_XBUTTONDBLCLK, WM_NCXBUTTONDOWN, WM_NCXBUTTONUP, or
WM_NCXBUTTONDBLCLK, the high-order word specifies which X button was
pressed or released, and the low-order word is reserved. This value
can be one or more of the following values. Otherwise, mouseData is
not used. Value Meaning XBUTTON1 0x0001 The first X
button was pressed or released. XBUTTON2 0x0002 The second X
button was pressed or released.
So a simplified version of your callback could look like that:
#include <iostream>
#include <type_traits> // std::make_signed_t<>
#include <windows.h>
LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode != HC_ACTION) // Nothing to do :(
return CallNextHookEx(NULL, nCode, wParam, lParam);
MSLLHOOKSTRUCT *info = reinterpret_cast<MSLLHOOKSTRUCT*>(lParam);
char const *button_name[] = { "Left", "Right", "Middle", "X" };
enum { BTN_LEFT, BTN_RIGHT, BTN_MIDDLE, BTN_XBUTTON, BTN_NONE } button = BTN_NONE;
char const *up_down[] = { "up", "down" };
bool down = false;
switch (wParam)
{
case WM_LBUTTONDOWN: down = true;
case WM_LBUTTONUP: button = BTN_LEFT;
break;
case WM_RBUTTONDOWN: down = true;
case WM_RBUTTONUP: button = BTN_RIGHT;
break;
case WM_MBUTTONDOWN: down = true;
case WM_MBUTTONUP: button = BTN_MIDDLE;
break;
case WM_XBUTTONDOWN: down = true;
case WM_XBUTTONUP: button = BTN_XBUTTON;
break;
case WM_MOUSEWHEEL:
// the hi order word might be negative, but WORD is unsigned, so
// we need some signed type of an appropriate size:
down = static_cast<std::make_signed_t<WORD>>(HIWORD(info->mouseData)) < 0;
std::cout << "Mouse wheel scrolled " << up_down[down] << '\n';
break;
}
if (button != BTN_NONE) {
std::cout << button_name[button];
if (button == BTN_XBUTTON)
std::cout << HIWORD(info->mouseData);
std::cout << " mouse button " << up_down[down] << '\n';
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
Regarding your main():
Since your application has no windows, no messages will be sent to it and GetMessage() will never return. This renders the message pump youseless. A single call to GetMessage() is sufficient to give Windows the opportunity to call the installed hook callback. What is a problem though, is, that Code after the call to GetMessage() will never get executed because the only ways to end the program are closing the window or pressing Ctrl + C.
To make sure UnhookWindowsHookEx() gets called, I'd suggest setting a ConsoleCtrlHandler:
HHOOK hook = NULL;
BOOL WINAPI ctrl_handler(DWORD dwCtrlType)
{
if (hook) {
std::cout << "Unhooking " << hook << '\n';
UnhookWindowsHookEx(hook);
hook = NULL; // ctrl_handler might be called multiple times
std::cout << "Bye :(";
std::cin.get(); // gives the user 5 seconds to read our last output
}
return TRUE;
}
int main()
{
SetConsoleCtrlHandler(ctrl_handler, TRUE);
hook = SetWindowsHookExW(WH_MOUSE_LL, MouseHookProc, nullptr, 0);
if (!hook) {
std::cerr << "SetWindowsHookExW() failed. Bye :(\n\n";
return EXIT_FAILURE;
}
std::cout << "Hook set: " << hook << '\n';
GetMessageW(nullptr, nullptr, 0, 0);
}