Win API: How to catch every message from user input? - c++

I want to catch:
Window resize/move/minimize/maximize/close messages.
Mouseclicks and keyboard presses.
When any program was executed by the user either pressing enter or dblclick. (if possible?)
This should work the same way as the keylock programs works: if you do some event, i can decide via my program will i let Windows handle it, or do i handle it, or both.
How can i do this?

As Hans Passant pointed out, you need the SetWindowsHookEx function.
In the link all possible hooks are explained in detail and the hook functions you need to implement are as well. Here is a small example, how to install a global hook that will process messages, after they are processed by the window.
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){
HHOOK msgHook = SetWindowsHookEx(WH_CALLWNDPROCRET, msgHook, hInstance, 0);
if(msgHook == NULL){
//Error handling here
cout << "Failed to set hook";
}
else{
//Hook has been set and will automatically be removed, when your application exits.
}
//A clean shutdown should always unhook everything it has installed
UnhookWindowsHookEx(msgHook);
return 0;
}
You can look up the hook functions definition in the MSDN, but it could look like this:
LRESULT CallWndRetProc(int nCode, WPARAM wParam, LPARAM lParam){
CWPRETSTRUCT* theMessage = (CWPRETSTRUCT*)lParam;
//now you can read all message parameters and the return value
//...
//Always return by calling the next hook in the chain
return CallNextHookEx(0, nCode, wParam, lParam);
}
The other hooks that you want to install follow the same principle.
See also
Hooks Overview
Using Hooks

Related

Win32 keyboardHook Callback receiving VK_LCTRL event whenever the top window is changed

I am using the Windows API on Windows7 with Visual Studio 2019.
I'm trying to set a low level keyboard hook and read the key input to the console, to do so I use the function: HHOOK SetWindowsHookExA( [in] int idHook, [in] HOOKPROC lpfn, [in] HINSTANCE hmod, [in] DWORD dwThreadId )
with the argument: WH_KEYBOARD_LL so I can monitor keyboard events.
This is my main function:
int main()
{
...
HHOOK keyBoardHook = SetWindowsHookExW(WH_KEYBOARD_LL, keyboardHook, NULL, 0);
MSG message;
while (true)
{
while(PeekMessage(&message, NULL, 0, 0, PM_REMOVE) != 0)
{
DispatchMessage(&message);
}
}
...
UnhookWindowsHookEx(keyBoardHook);
return 0;
}
And this is the callback implementation:
LRESULT CALLBACK keyboardHook(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
KBDLLHOOKSTRUCT* keyData = (KBDLLHOOKSTRUCT*)lParam;
if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)
{
fileHandler.addStr(keyHandler.translate(keyData,true,topWindowThreadId));
}
else if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP)
{
fileHandler.addStr(keyHandler.translate(keyData,false,topWindowThreadId));
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
How both variables work is not the issue here but just know that
in the callback keyHandler translates the message and fileHandler prints the result to a file.
Here comes the issue. The callback receives:
a key down event when I press a key
a key up event when I release
a repeated key down event when I hold a key
a control key up event VK_LCONTROL when focused window changes
Why does that happen ? This behavior is never mentioned in the MSDN documentation as far as I know. If I switch from console window or any window to any other window this event is generated and a [left control up] message is printed by my keyHandler !
Now to make sure it's not a problem with my code I changed the Callback to this:
LRESULT CALLBACK keyboardHook(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
KBDLLHOOKSTRUCT* keyData = (KBDLLHOOKSTRUCT*)lParam;
if (keyData->vkCode == VK_LCONTROL)
{
printf("CTRL");
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
And still CTRL is printed to the consol whenever I change the focused window, Note that only VK_LCONTROL is generated and wParam is always WM_KEYUP
This is not supposed to happen!
I tried analyzing and modifying my code, tried removing everything from the main function until it looked just like in the example above even rebooted the computer to check if it's an internal issue, nothing helps. and when searching the web for a similar post with a similar issue I found absolutely none, which makes it even more confusing.
Update
I tried compiling someone else's keyboard-hook related code from GitHub, same issue.. is this normal ? Why is it not documented ?
According to the description of the SetWindowsHookExA in the official Microsoft documentation.
The global hooks are a shared resource, and installing one affects all applications in the same desktop as the calling thread. All global hook functions must be in libraries. Global hooks should be restricted to special-purpose applications or to use as a development aid during application debugging. Libraries that no longer need a hook should remove its hook procedure.
HHOOK SetWindowsHookExA(
[in] int idHook,
[in] HOOKPROC lpfn,
[in] HINSTANCE hmod,
[in] DWORD dwThreadId
);
For desktop apps, if this parameter(dwThreadId) is zero, the hook procedure is associated with all existing threads running in the same desktop as the calling thread. That's why after you changed the focused window, CTRL still printed to the console. So it is recommended that you use hook in DLL.

KeyboardProc Callback function not getting called?

So I am trying to make a message box appear when a user presses a button on his/her keyboard using Hooks.
The hook is getting installed correctly because there are no errors, but it seems like the KeyboardProc Callback function is not getting called because the message box that is supposed to show up when it is called never shows up.
There are no errors btw that show up and I am programming this in a desktop app.
Here is the code regarding the hook and the callback function:
LRESULT CALLBACK KeyboardProc(
int nCode, WPARAM keyState, LPARAM keyInfo) {
LRESULT reValue = 0;
MessageBox(hWnd, L"Testing", L"Test", MB_OK);//This is the msg box that isnt showing up
if (nCode < 0) {
reValue = CallNextHookEx(keyboardHook, nCode, keyState, keyInfo);
}
return reValue;
};
keyboardHook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)KeyboardProc, hInstance, 0);
You need to run a message loop to process the calls. From the remakrs on the KeyboardProc callback function description: "The call is made by sending a message to the thread that installed the hook. Therefore, the thread that installed the hook must have a message loop."

MouseHook to detect when mouse is moving

I am trying to create a simple mouse hook to detect if the mouse is moving, but for some reason when I run the program, the mouse doesnt function at all until I stop the process.
Here is my code:
#include <windows.h>
HHOOK g_hMouse;
LRESULT CALLBACK MouseHook(int nCode, WPARAM wParam, LPARAM lParam)
{
printf("MOUSE EVENT!\n");
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
int main()
{
g_hMouse = SetWindowsHookEx(WH_MOUSE_LL, MouseHook, NULL, NULL);
while (1) {
Sleep(2);
}
return 0;
}
Any help would be appreciated.
Thanks.
WM_MOUSE_LL hooks require that the thread that installed it keeps pumping messages; so you'll need a GetMessage/DispatchMessage loop here. Details for this are in the MSDN docs for WM_MOUSE_LL:
This hook is called in the context of the thread that installed it. The call is made by sending a message to the thread that installed the hook. Therefore, the thread that installed the hook must have a message loop.
If you just want to try something quick when experimenting/debugging, replace your Sleep() with a call to MessageBox(...), which will block your code so you can do testing, but it runs its own message loop, so the hook will run.

role of message loop in this program ? and few more questions

The following code works fine. It gives out the message when the user presses a key. But there are certain things I am not aware of. What is the role of Message Loop here ? I read that calling SetWindowsHookEx(...) registers a function with the windows and windows calls the appropriate function automatically when a event of registered type happens. No doubt that i don't see the output if don't give the message loop it's space.
#include<iostream>
#include <windows.h>
using namespace std;
HINSTANCE hinst = NULL;
static HHOOK handleKeyboardHook = NULL;
static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
void setWinHook() {
handleKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc,NULL, 0);
if(handleKeyboardHook == NULL) {
cout << "is NULL";
} else {
cout << "is not NULL";
}
cout<<("Inside function setWinHook !");
}
static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
cout << ("You pressed a key !\n");
return CallNextHookEx(handleKeyboardHook, nCode, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
handleKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, hInstance, 0);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
EDIT
Does exiting the program (closing the console window) unregister the hook ?
EDIT 2
what role does Dispatch Message play here ? According to doc it dispatches a message to window procedure,but here even if i exclude that,it doesn't effect the output.
All events in Windows, even the low-level keyboard event used in your example, is sent using the normal message events. So for the program to be able to sense keyboard events, it has to use an event loop processing messages.
Without a loop, the program would exit immediately, and the hook would be removed at once too. You cannot register a hook and exit — the system would become a mess if buggy programs were leaving too many forgotten hooks after them. Once your process dies, the hook is scheduled for removal.
I don't remember about low-level keyboard hook, but callbacks of many other hooks are only called inside GetMessage/PeekMessage, and not on some other thread, so just an infinite loop won't suffice — it has to be a message loop.
"what role does Dispatch Message play here ? According to doc it dispatches a message to window procedure,but here even if i exclude that,it doesn't effect the output."
DispatchMessage is pretty useless cos console window doesnt receive much messages.
Only message received is when window loses focus.

Set location of MessageBox?

I want to print out a message using MessageBox (or similar). I would also like control over where exactly on the screen the box appears but can find nothing in the description of MessageBox that allows you control over the location. Did I miss something? If MessageBox can not be used, then is there an alternative?
For reasons too complex to go into here, I would prefer an answer which didn't involve making my own window and passing the address of a callback function.
Step 1: Create a CBT hook to trap the creation of the message box:
// global hook procedure
HHOOK hhookCBTProc = 0;
LRESULT CALLBACK pfnCBTMsgBoxHook(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HCBT_CREATEWND)
{
CREATESTRUCT *pcs = ((CBT_CREATEWND *)lParam)->lpcs;
if (pcs->style & WS_DLGFRAME || pcs->style & WS_POPUP)
{
HWND hwnd = (HWND)wParam;
// At this point you have the hwnd of the newly created
// message box that so you can position it at will
SetWindowPos(hwnd, ...);
}
}
return CallNextHookEx(hhookCBTProc, nCode, wParam, lParam);
}
Step 2: Install/remove the hook before and after showing the message box:
// set hook to center the message box that follows
hhookCBTProc = SetWindowsHookEx(WH_CBT,
pfnCBTMsgBoxHook,
0, GetCurrentThreadId());
int sResult = MessageBox(hwndParent, pszMsg, pszTitle, usStyle);
// remove the hook
UnhookWindowsHookEx(hhookCBTProc);
If I needed additional behavior for a Messagebox I always created my own window and made it look like a standard MessageBox. You do it right once and you can always reuse it in other projects.
MessageBox is a basically a set of defaults. Don't like them? Bring your own. If you don't want a real window with all its complexities, but MessageBox is too restrictring, create a dialog.
You could do this with a CBT hook procedure. There is an MSDN article on how to do this in VB but converting it to C++ wouldn't be hard.
http://support.microsoft.com/kb/180936