I have a working Windows hook to detect the simple keypress combinations of both LCTRL + x, and LCTRL + v. The only problem I have is when I press LCTRL, then release, then press x or v, my program thinks they are still being pressed together.
Here is my Hook Callback function:
HHOOK hookHandle;
KBDLLHOOKSTRUCT hookdata;
LRESULT __stdcall HookCallback(int code, WPARAM wparam, LPARAM lparam)
{
if (code >= 0)
{
if (wparam == WM_KEYDOWN)
{
hookdata = *((KBDLLHOOKSTRUCT*)lparam);
switch(hookdata.vkCode)
{
case(88):
if(GetAsyncKeyState(VK_LCONTROL))
{
std::cout<<"CTRL X PRESSED";
}
break;
case (86):
if(GetAsyncKeyState(VK_LCONTROL))
{
std::cout<<"CTRL V PRESSED";
}
break;
}
}
}
return CallNextHookEx(hookHandle, code, wparam, lparam);
}
Example Input
Test 1) Press LCTRL, Press X
Test 2) Press LCTRL, Release LCTRL, Press X
Expected Output
Test 1) CTRL X PRESSED
Test 2) No Output
Actual Output
Test 1) CTRL X PRESSED (WORKING)
Test 2) CTRL X PRESSED (NOT WORKING)
You're incorrectly checking GetAsyncKeyState's return value:
If the function succeeds, the return value specifies whether the key was pressed since the last call to GetAsyncKeyState, and whether the key is currently up or down. If the most significant bit is set, the key is down, and if the least significant bit is set, the key was pressed after the previous call to GetAsyncKeyState. However, you should not rely on this last behavior; for more information, see the Remarks.
Change the check to GetAsyncKeyState(...) & 0x8000 to fix that by checking only the highest bit.
A small thing: Microsoft made the VK values the same as the ASCII code. Since complete portability is of small concern here, you can use 'X' and 'V' instead of 88 and 86 to better express intent.
Related
Edit 3: This is no good--the Right arrow key is sent every time the window loses focus--bad behavior.
I tried intercepting when the user clicks the close box, but so far I have not been able to find it in the class wizard. If anyone knows how to detect this event, please let me know.
Original post(s):
Edit 2: I have edited the code for a solution that does not use a magic number, but as Mark Ransom has noted, this is not the proper way to select a specific button for an input prompt, so this may work correctly, but it is a hack.
I have a legacy application in MFC that has a prompt when exiting to save changes. When the input prompt opens, the 'No' button should be selected when it prompts the user to save changes in the CMainFrame window. This is so that the user can press the Enter key to exit without having to use the mouse to select it. And yes, I am aware that the user can simply press the 'N' key to activate the 'No' button, but the 'No' button needs be selected so the user can press the Enter key to exit.
Using the message loop in:
BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam)
I can detect a message when the window is closing, before being prompted to save changes.
So I added this numeric value and I am sending the right arrow key to select 'No' in the input message prompt.
EDIT: Added beginning of switch statement to show what is being evaluated.
// this is part of 'BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam)'
BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam)
{
switch( wParam )
{
...
//case 33614080:
//SendRightArrowKey();
//break;
default:
WORD loWord = LOWORD(wParam);
if (ID_RECORD_FIRST == loWord)
{
WORD HiWord = HIWORD(wParam);
unsigned short value = HiWord;
char buff[32];
_itoa(value, buff, 10);
if (EN_KILLFOCUS == value )
{
SendRightArrowKey();
}
return TRUE;
}
}
return CFrameWnd::OnCommand(wParam, lParam);
}
void SendRightArrowKey()
{
INPUT output[2];
ZeroMemory(&output, sizeof(output));
// Right arrow key down
output[0].type = INPUT_KEYBOARD;
output[0].ki.wVk = VK_RIGHT;
output[0].ki.time = 0;
output[0].ki.dwExtraInfo = 0;
// Right arrow key up
output[1].type = INPUT_KEYBOARD;
output[1].ki.wVk = VK_RIGHT;
output[1].ki.dwFlags = KEYEVENTF_KEYUP;
output[1].ki.time = 0;
output[1].ki.dwExtraInfo = 0;
int retval1 = SendInput(2, output, sizeof(INPUT));
}
This works for win32 and x64, but it feels like a hack--especially because I can't find any definition for the numeric code 33614080. There should be constant or a macro for the windows message that gets sent for the WParam, in case Microsoft ever changes this value.
Does anyone know of a constant that this WParam value might represent,
or another way to have the 'No' button selected by default?
Thanks in advance for any replies.
Thanks Adrian for figuring out the values.
I am programming a really simple MFC C++ Application which is working with HotKeys. To set a HotKey I use the following method from the WinAPI for my application:
BOOL RegisterHotKey(
HWND hWnd, // window to receive hot-key notification
int id, // identifier of hot key
UINT fsModifiers, // key-modifier flags
UINT vk // virtual-key code
);
To catch any HotKey Message I use: ON_MESSAGE(WM_HOTKEY,OnHotKey) in the message map and this Callback Method to test it's functionality:
LRESULT OnHotKey(WPARAM wParam, LPARAM lParam)
{
if (wParam == MY_HOTKEY_KEY_CODE)
{
MessageBox(L"HotKey was pressed!");
return TRUE;
}
return FALSE;
}
When a HotKey is pressed it enters the OnHotKey Method and does not process the key normally. For example if I write some text in Notepad and press "O" as HotKey, the "O" wont append to my text but the Message "HotKey was pressed!" appears, which is nice.
But when I am in any Direct X Game and press my HotKey, it is not sent to my application. Also when typing something in a Direct X environment the HotKey just works as normal key.
Is Direct X binding all key inputs somehow? Is there a way to make Windows HotKeys work with Direct X environments?
I've done a lot of searching around with no real solution (to my own problem) so I thought I'd ask here.
I'm designing a kiosk-like program that prevents the user from using task keys (alt+tab, alt+esc, ctrl+esc, etc) while the program is running. Note I'm a novice programmer thus I'd want to stay away from separate dll handling if I can. Particularly, I have went to this site http://support.microsoft.com/kb/226359/en-us for the code. A simplified part of my code looks like this at the top:
HHOOK mule;
HHOOK g_hKeyboardHook;
BOOL g_bFullscreen;
LRESULT CALLBACK LowLevelKeyboardProc (INT nCode, WPARAM wParam, LPARAM lParam)
{
// By returning a non-zero value from the hook procedure, the
// message does not get passed to the target window
KBDLLHOOKSTRUCT *pkbhs = (KBDLLHOOKSTRUCT *) lParam;
BOOL bControlKeyDown = 0;
switch (nCode)
{
case HC_ACTION:
{
// Check to see if the CTRL key is pressed
bControlKeyDown = GetAsyncKeyState (VK_CONTROL) >> ((sizeof(SHORT) * 8) - 1);
// Disable CTRL+ESC
if (pkbhs->vkCode == VK_ESCAPE && bControlKeyDown)
return 1;
// Disable ALT+TAB
if (pkbhs->vkCode == VK_TAB && pkbhs->flags & LLKHF_ALTDOWN)
return 1;
// Disable ALT+ESC
if (pkbhs->vkCode == VK_ESCAPE && pkbhs->flags & LLKHF_ALTDOWN)
return 1;
break;
}
default:
break;
}
return CallNextHookEx (mule, nCode, wParam, lParam);
}
The my main is
int main(int argc, char **argv)
{
_getch();
g_hKeyboardHook = SetWindowsHookEx( WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), 0 );
cout << "Testing task keys disabling (alt tab, ctrl esc, alt esc) and taskbar..." << endl;
_getch();
UnhookWindowsHookEx( g_hKeyboardHook );
cout << "Re enabled" << endl;
_getch();
return 0;
}
I realize this code is for really old windows OS, but I've looked around and the other solutions resemble this code so I thought it should work.
But for some reason it doesn't seem to be working. Whenever my program gets to that line of code, the program stalls for like 5 seconds and continues to run, but the task keys are still working.
I've heard that I should be implementing that function as a dll instead of putting everything in one file, but I'm not sure if they're absolutely right (also I know nothing of dlls)
In addition, I've also tried code (to disable windows key) here: http://msdn.microsoft.com/en-us/library/windows/desktop/ee416808(v=vs.85).aspx and it does the same thing my own program (stalls and does nothing)
Can anyone spot where I did something wrong? I'm using VC++ 2010 on windows 7 64bit.
Your code is fine, hooks just doesn't work with console application because windows can't callback into a console application, it requires a message loop.
Read this answer by Hans Passant which applies here too.
I want to get mouse click coordinates X and Y when user press Ctrl and click at same time.
User may click anywhere on the screen or programs. I want my program to catch the event and get coordinates when Ctrl key is down pressed and mouse click occurs at same time. I want to get system coordinates X and Y, not the window coordinates of other programs.
I'm using C++ .
How to do that ?
Windows OS, WIN API code
I'm doing next which is not working:
HHOOK MouseHook;
LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam){
PKBDLLHOOKSTRUCT k = (PKBDLLHOOKSTRUCT)(lParam);
POINT p;
if(wParam == WM_RBUTTONDOWN)
{
// right button clicked
GetCursorPos(&p);
//p.x
//p.y
//my program is never getting here, why ?
}
}
MouseHook = SetWindowsHookEx(WH_MOUSE_LL,(HOOKPROC)MouseHookProc,0,0);
if I change the above line to: MouseHook = SetWindowsHookEx(WH_MOUSE,(HOOKPROC)MouseHookProc,GetModuleHandle(NULL),0);
then it will work only for my own program window, but not hooking clicks outside of my program
Have you read this article? link
First of all, if you want to catch system entire mouse + keyboard event, your hooking function must be placed on a dll.
1)If nCode is less than zero, the hook procedure must return the value returned by CallNextHookEx.
Like below,
if(nCode < 0)
{
CallNextHookEx(hook, nCode, wParam, lParam);
return 0;
}
If CallNextHookEx is not called, there is a chance that other processes that have installed hook may not get the events correctly.
2)And then, check nCode again, whether it is HC_ACTION.
switch (nCode)
{
case HC_ACTION:
...
3)Finally, you can check WPARAM for WM_RBUTTONDOWN, and LPARAM for MSLLHOOKSTRUCT
The way I would approach is I would create a global variable say
int ctrl_pressed = 0
/*
0 -- ctrl nt pressed
1 -- Crtl Pressed
*/
Step 1: Now I would handle both WM_KEYDOWN and WM_KEYUP for ctrl
Step 2: In the callback of WM_KEYDOWN (if lparam is ctrl ) I would set ctrl_pressed to 1.
And in case of WM_KEYUP I would set ctrl_pressed to 0.
Step 3: Now Finally ill handle WM_MOUSECLICKED
and then check if ctrl_pressed(global) is 1 , which if true I can just get the coordinates.
This is just an approach may be not the most efficient solution.
Wait for more experienced WIN32 Developers to give their input on this.
I have a win 32 application written in c++ which sets the low level keyboard hook. now i want to sendInput to any app like word / notepad. how do i do this?
i have already done enough of using findwindow / sendmessage. for all these, i need to know edit controls. finding the edit control is very difficult.
since SendInput works for any windows application, i want to use it. the problem is i get a call to my callback function with the pressed key.
for e.g i pressed A and i want to send U+0BAF unicode character to the active applciation windows. in this case, assume it is notepad.
the problem is i get two characters U+0BAF and A in the notepad.
A is being sent because i am calling CallNextHookEx( NULL, nCode, wParam, lParam);
if i return 1 after sendInput, then nothing is sent to notepad.
any suggestion?
If I understood your problem correctly, you should ignore "injected" key events in your hook procedure, like this:
LRESULT CALLBACK
hook_proc( int code, WPARAM wParam, LPARAM lParam )
{
KBDLLHOOKSTRUCT* kbd = (KBDLLHOOKSTRUCT*)lParam;
// Ignore injected events
if (code < 0 || (kbd->flags & LLKHF_INJECTED)) {
return CallNextHookEx(kbdhook, code, wParam, lParam);
}
...
Update: additionally, you have to eat characters and notify some other routine for a character press through Windows messages.
Example:
...
// Pseudocode
if (kbd->vkCode is character) {
if (WM_KEYDOWN == wParam) {
PostMessage(mainwnd, WM_MY_KEYDOWN, kbd->vkCode, 0);
return 1; // eat the char, ie 'a'
}
}
return CallNextHookEx(kbdhook, code, wParam, lParam);
And, in some other module, you handle WM_MY_KEYDOWN:
ie, #define WM_MY_KEYDOWN (WM_USER + 1)
and call the appropriate routine that will generate new key events.