I have just begun to learn how to set low level keyboard hooks in Windows using C++.
Here is a small code I have written to get started.
#include <iostream>
#include <windows.h>
HHOOK hook;
LRESULT CALLBACK keyboardHook(int nCode, WPARAM wParam, LPARAM lParam)
{
KBDLLHOOKSTRUCT *p = (KBDLLHOOKSTRUCT *) lParam;
std::cout << nCode << " - " << wParam << " - " << p->vkCode << std::endl;
return CallNextHookEx(hook, nCode, wParam, lParam);
}
int main(int argc, char **argv)
{
hook = SetWindowsHookEx(WH_KEYBOARD_LL, keyboardHook, NULL, NULL);
if (hook == NULL) {
std::cout << "Error " << GetLastError() << std::endl;
return 1;
}
MSG messages;
std::cout << "Waiting for messages ..." << std::endl;
while (GetMessage (&messages, NULL, 0, 0))
{
std::cout << "Got message" << std::endl;
TranslateMessage(&messages);
std::cout << "Translated message" << std::endl;
DispatchMessage(&messages);
std::cout << "Dispatched message" << std::endl;
}
return 0;
}
I compile this in this manner:
vcvars32.bat
cl /D_WIN32_WINNT=0x0401 foo.cc /link user32.lib
When I execute the code and press A followed by B I get an output like the following:
C:\>foo
Waiting for messages ...
0 - 257 - 13
0 - 256 - 65
0 - 257 - 65
0 - 256 - 66
0 - 257 - 66
Why do I never see Got message in the output? Why does the control never enter the while loop? Can someone help me understand the purpose of GetMessage, TranslateMessage and DispatchMessage here? I have read the documentation, but I guess I am missing something because I fail to understand how they are useful since I am never able to invoke the body of the while loop.
The GetMessage() function waits until a message arrives for one of the windows created by your process. Since you have created no windows, you will receive no messages.
Related
I tried to implement a basic hook under windows.
While it technically works (my handle gets called) it produces massive bugs, that i do not know how to fix.
When I run the programm and try to write something in the firefox.exe searchbar or the explorer.exe addres bar the programs crash.
That is the code i currently tried. I stripped everything unnecessary but it still doesnt work
main.cc
#include <iostream>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <shellapi.h>
int main (int argc, char** argv){
HINSTANCE dll = NULL;
HOOKPROC proc = NULL;
HHOOK hook = NULL;
MSG msg;
BOOL b_ret = FALSE;
dll = LoadLibraryA("hookdll.dll");
if (dll == NULL) {
std::cerr << "WinAPI Error: " << GetLastError() << "\n";
return 1;
}
proc = (HOOKPROC)GetProcAddress(dll, "KeyboardHook");
if (proc == NULL) {
FreeLibrary(dll);
std::cerr << "WinAPI Error: " << GetLastError() << "\n";
return 1;
}
hook = SetWindowsHookEx(WH_CALLWNDPROC, proc, dll, 0);
if (hook == NULL) {
FreeLibrary(dll);
std::cerr << "WinAPI Error: " << GetLastError() << "\n";
return 1;
}
while (GetMessage(&msg, NULL, WH_KEYBOARD, WM_KEYLAST) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnhookWindowsHookEx(hook);
FreeLibrary(dll);
return 0;
}
And in the DLL:
hooks.h
#ifndef __LOG_DLL_HOOKS_H__
#define __LOG_DLL_HOOKS_H__
#include <iostream>
#include <fstream>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
extern "C" LRESULT CALLBACK KeyboardHook(
_In_ int code,
_In_ WPARAM wParam,
_In_ LPARAM lParam);
#endif // !__LOG_DLL_HOOKS_H__
hooks.cc
#include "Hooks.h"
extern "C" LRESULT CALLBACK KeyboardHook(
_In_ int code,
_In_ WPARAM wParam,
_In_ LPARAM lParam) {
if (code == HC_ACTION) {
}
return CallNextHookEx(NULL, code, wParam, lParam);
}
As you can see it barely does anything.
I worked with the official windows docs and did everything the right way (or so i thought)
The only thing that could make a difference is the return value of KeyboardHook, but
https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms644984(v=vs.85) states that:
If code is less than zero, the hook procedure must return the value returned by CallNextHookEx.
If code 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;
I read that as: "Return CallNextHookEx either way", which i did.
Thanks for any answeres
Well, for starters, why are you using a keyboard function for a window procedure hook? I think you meant to use WH_KEYBOARD instead of WH_CALLWNDPROC when calling SetWindowsHookEx().
Also, your GetMessage() call is wrong, because WH_KEYBOARD is not a message identifier. You would need to use WM_KEYFIRST instead, to match your use of WM_KEYLAST (since you are clearly only interested in dispatching keyboard messages).
However, you are setting the dwThreadId parameter of SetWindowsHookEx() to 0, which means you are hooking not just your own thread but all threads of all processes globally. A WH_KEYBOARD hook runs in the context of the thread that installs it, which means internally the OS will have to delegate the hooked keyboard messages of those other threads to your thread, and it does that by sending messages to your thread. But, your message loop is not going to be processing any of those messages because it is filtering for only keyboard messages (of which it will never receive, since your thread has no UI of its own).
With all of that said, try this instead:
#include <iostream>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <shellapi.h>
int main (){
HINSTANCE dll = LoadLibraryA("hookdll.dll");
if (dll == NULL) {
std::cerr << "WinAPI Error: " << GetLastError() << "\n";
return 1;
}
HOOKPROC proc = (HOOKPROC) GetProcAddress(dll, "KeyboardHook");
if (proc == NULL) {
std::cerr << "WinAPI Error: " << GetLastError() << "\n";
FreeLibrary(dll);
return 1;
}
HHOOK hook = SetWindowsHookEx(WH_KEYBOARD, proc, dll, 0);
if (hook == NULL) {
std::cerr << "WinAPI Error: " << GetLastError() << "\n";
FreeLibrary(dll);
return 1;
}
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnhookWindowsHookEx(hook);
FreeLibrary(dll);
return 0;
}
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'm trying to create independent window wrapper classes for my project. It's mostly working but cannot figure out how to get WM_QUIT in my main message pump. In the interest of learning Windows, I don't want to use other libraries for this.
This is a quick example of whats happening.
#include <iostream>
#include <Windows.h>
void TestPump()
{
MSG msg = { 0 };
PostQuitMessage(0);
std::cout << "Posted WM_QUIT" << std::endl;
while (true)
{
BOOL result = PeekMessage(&msg, (HWND) -1, 0, 0, PM_REMOVE);
std::cout << "PeekMessage returned " << result << std::endl;
if (result == 0)
break;
if (WM_QUIT == msg.message)
std::cout << "got WM_QUIT" << std::endl;
}
}
void MakeWindow()
{
auto hwnd = CreateWindowEx(0, "Button", "dummy", 0, 0, 0, 32, 32, NULL, NULL, NULL, NULL);
std::cout << std::endl << "Created Window" << std::endl << std::endl;
}
int main()
{
TestPump();
MakeWindow();
TestPump();
std::cin.get();
return EXIT_SUCCESS;
}
The PeekMessage documentation is here: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644943(v=vs.85).aspx
I haven't been able to find any examples of using a -1 HWND filter, but MSDN says that it'll receive thread messages where the HWND is NULL (I've checked that this is true for WM_QUIT), which I believe PostQuitMessage does with WM_QUIT.
The problem only occurs if I create a Window.
Is there anything I'm doing wrong, or are there better methods?
This seems to be an interesting case so I've created a mcve, though I'm struggling to explain this behavior:
#include <Windows.h>
#include <CommCtrl.h>
#include <iostream>
#include <cassert>
void
Create_ThreadMessagePump(void)
{
::MSG msg;
::PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
::std::wcout << L"initialized thread message pump" << ::std::endl;
}
void
Create_Window(void)
{
auto const hwnd{::CreateWindowExW(0, WC_STATICW, L"dummy window", 0, 0, 0, 32, 32, NULL, NULL, NULL, NULL)};
(void) hwnd; // not used
assert(NULL != hwnd);
::std::wcout << L"created window" << ::std::endl;
}
void
Test_QuitMessageExtraction(void)
{
::PostQuitMessage(0);
::std::wcout << L"posted WM_QUIT" << ::std::endl;
for(;;)
{
::MSG msg;
auto const result{::PeekMessageW(&msg, (HWND) -1, 0, 0, PM_REMOVE)};
::std::wcout << L"PeekMessageW returned " << result << ::std::endl;
if(0 == result)
{
::std::wcout << L"no more messages to peek" << ::std::endl;
break;
}
if(WM_QUIT == msg.message)
{
::std::wcout << L"got WM_QUIT" << ::std::endl;
}
}
}
int
main()
{
//Create_ThreadMessagePump(); // does not change anything
Test_QuitMessageExtraction();
Create_Window();
Test_QuitMessageExtraction();
system("pause");
return(0);
}
output:
posted WM_QUIT
PeekMessageW returned 1
got WM_QUIT
PeekMessageW returned 0
no more messages to peek
created window
posted WM_QUIT
PeekMessageW returned 0
no more messages to peek
The problem appears to be with the way WM_QUIT and PostQuitMessage() operates. You can find information here:
Why is there a special PostQuitMessage function?.
In short, the WM_QUIT message will not be received while the queue is non-empty. Even if you filter out other messages by using a -1 HWND, the queue is still non-empty, and thus WM_QUIT is not returned. Creating of a window results in multiple HWNDs being created.
I'll preface this with saying that I am new to win32 programming.
I'm creating a global keyboard hook using a .dll, I have put in install and uninstall functions to handle the actual setting and removing of the keyboard hook.
#pragma comment(linker, "/SECTION:.SHARED,RWS")
#pragma data_seg(".SHARED")
static HHOOK hk=NULL;
//static CMyFile *pLF;
#pragma data_seg()
HINSTANCE hins = NULL;
__declspec( dllexport ) LRESULT Install(){
std::cout << "Installing" << std::endl;
hk = SetWindowsHookEx(WH_KEYBOARD_LL,EventAnalysis::KeystrokeAnalysis::KeyboardCallback,hins,0);
if(hk == NULL){
std::cout << "Hook creation failed! - " << GetLastError() << std::endl;
}
return 0;
}
__declspec(dllexport) BOOL CALLBACK UnInstall()
{
std::cout << "UnInstalling" << std::endl;
return UnhookWindowsHookEx(hk);
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
hins = (HINSTANCE) hModule;
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
So now that I have my .dll, I created a simple executable that loads the library and installs the hooks:
int _tmain(int argc, _TCHAR* argv[])
{
auto funHandle = LoadLibrary(L"C:\\Users\\tprodanov\\Documents\\visual studio 2010\\Projects\\HaveFun\\Release\\HaveFun.dll");
if(funHandle == NULL){
std::cout << "Library load failed! - " << GetLastError() << std::endl;
}
auto Install = (LRESULT(*)()) GetProcAddress(funHandle, "?Install##YAJXZ");
if(Install == NULL){
std::cout << "Procedure load failed! - " << GetLastError() << std::endl;
}
Install();
MSG Msg;
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
auto Uninstall = (BOOL(*)()) GetProcAddress(funHandle, "?UnInstall##YGHXZ");
if(Uninstall == NULL){
std::cout << "Procedure load failed! - " << GetLastError() << std::endl;
}
Uninstall();
return 0;
}
What I find curious is that the behavior of my program is as expected in its current state (and also if instead of a message loop I just pop out a MessageBox() that waits for the user to click OK), but it doesn't work if I use std::cin.get() or an empty while loop. Can someone please explain why this is the behavior?
Also as a followup question - the KeyboardCallback function just prints to the console "Key Pressed".
LRESULT CALLBACK EventAnalysis::KeystrokeAnalysis::KeyboardCallback(int nCode, WPARAM wParam, LPARAM lParam){
std::cout << "Key pressed" << std::endl;
return CallNextHookEx(NULL,nCode,wParam,lParam);
}
I expected that this will be printed only when I press keys when focused on the console window, but even if I type in notepad the "Key Pressed" messages show up in my executable that called the Install function of the .dll. I don't understand this, since as far as I understood the dynamic library is loaded independently in every process, so every process has its own copy of the KeyboardCallback function and it will try to print to the foreground window's console.
The documentation makes it clear what is happening:
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.
The hook is installed by the call you made to Install. And so in the same thread that makes that call you need to run a message loop.
As for why showing a message box influences things, a message box operates by running a modal message loop. And that message loop will dispatch the hook messages.
And for the followup question, again the documentation has the answer:
The system calls this function every time a new keyboard input event is about to be posted into a thread input queue.
When it says posted into a thread input queue it means any thread input queue.
Or in the remarks to the documentation of SetWindowsHookEx look at the list of the various hook types, and their scope. The WH_KEYBOARD_LL is listed as a global hook.
Note also that WH_KEYBOARD_LL is a low-level hook and so does not result in the DLL being injected into another process. In fact your code is overly complex. You simply do not need a DLL here at all. You can get it all going with a call to SetWindowsHookEx from your executable that passes a callback function in that same executable.
Here's a minimal sample program that demonstrates all this:
#include <Windows.h>
#include <iostream>
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
std::cout << "Key pressed" << std::endl;
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
int main(int argc, char* argv[])
{
std::cout << "Installing" << std::endl;
HHOOK hk;
hk = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, NULL, 0);
MSG Msg;
while (GetMessage(&Msg, NULL, 0, 0))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
std::cout << "UnInstalling" << std::endl;
UnhookWindowsHookEx(hk);
return 0;
}