How do I modify keys in a KeyboardProc hooking procedure? - windows

I can use the SetWindowsHookEx function to hook a custom KeyboardProc. In the hooked procedure I can return 1 to block the keyboard input from reaching the application, however what I'm trying to do is actually modify the data. E.g. the user hits the key A and I'd like to replace that with key B.
But any modifications I make to the WPARAM and LPARAM arguments do not seem to have any effect in the application, it will still receive the original keys. E.g. to show a little pseudocode:
LRESULT KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
memset(&wParam, 0, sizeof(WPARAM));
memset(&lParam, 0, sizeof(LPARAM));
// call the next procedure
return CallNextHookEx(keyHook, nCode, wParam, lParam);
}
No matter what modifications I make to the two parameters it will not have an effect, once the app reads the WM_KEYDOWN message it will receive the original keys as they were hit.
Is there any way I can actually make modifications to the keys?

The WH_KEYBOARD_LL hook enables you to monitor keyboard input events about to be posted in a thread input queue.
From: https://learn.microsoft.com/en-us/windows/win32/winmsg/about-hooks?redirectedfrom=MSDN#wh_debug
This means it is read-only and the params can not be modified.

The solution is to use SendInput, but only when lParam - which should be type-cast to KBDLLHOOKSTRUCT*) - has the LLKHF_INJECTED flag in its flags field.
Here's some D language code copied verbatim from one of my projects:
extern(Windows)
LRESULT LowLevelKeyboardProc(int code, WPARAM wParam, LPARAM lParam)
{
auto kbs = cast(KBDLLHOOKSTRUCT*)lParam;
// generate a new key event only if this key event was user-generated.
if (!(kbs.flags & LLKHF_INJECTED))
{
// Alt == toggle key binding
if (kbs.flags & LLKHF_ALTDOWN)
useKeyMap ^= 1;
if (!useKeyMap)
return CallNextHookEx(keyHook_LL, code, wParam, lParam);
INPUT input;
input.type = INPUT_KEYBOARD;
input.ki.dwFlags = (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) ? 0 : KEYEVENTF_KEYUP;
// replace the key, must be in range 1 to 254
input.ki.wVk = keyMap.get(cast(Key)kbs.vkCode, cast(Key)kbs.vkCode);
SendInput(1, &input, INPUT.sizeof);
return -1;
}
return CallNextHookEx(keyHook_LL, code, wParam, lParam);
}

Related

How do I tell what messages to handle when subclassing a control?

I want to subclass the edit control into a specific case of a masked edit - something that accepts five characters of user input, and displays colons after the first and third characters. I can imagine two basic approaches to this.
I could have the text that the edit control stores be the text I want displayed. In this case, I would need to set the text to L" : : " to begin with, and override the messages that detect user input so I could copy it into the correct slots in that string. However, I don't know how to be sure which messages those are. I assume WM_KEYDOWN is one, but if there are others, and I don't think of them all, input that triggers the messages I missed would incorrectly defer to the edit control's default handling.
Alternatively, I could have the text that the edit control stores be the text the user enters - no colons. In that case, I would need to override the way the control is displayed so I could to generate a string that includes colons based on the stored text, and draw that when drawing the control. I assume this would mean replacing the WM_PAINT handling. The problem with that is that it would seem to require redefining everything about how the control looks myself, when the text is the only part I want to change. I'm not confident I could do that perfectly, and I would certainly rather not.
How should I approach this?
Edit: I've tried overriding WM_PAINT like this:
INT_PTR CALLBACK MaskedEditProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam,
UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
if (message == WM_PAINT)
{
WCHAR userInput[6];
Edit_GetText(hwndDlg, userInput, 6);
WCHAR displayString[]{L" : : "};
int userInputLength{ Edit_GetTextLength(hwndDlg) };
switch (userInputLength)
{
case 5:
displayString[6] = userInput[4];
case 4:
displayString[5] = userInput[3];
case 3:
displayString[3] = userInput[2];
case 2:
displayString[2] = userInput[1];
case 1:
displayString[0] = userInput[0];
}
Edit_SetText(hwndDlg, displayString);
DefSubclassProc(hwndDlg, message, wParam, lParam);
Edit_SetText(hwndDlg, userInput);
return TRUE;
}
return DefSubclassProc(hwndDlg, message, wParam, lParam);
}
This seems to basically work, except for some reason it causes the displayed text to flicker.
Edit 2: I set the control's text to L"0:00:00" from its parent window, and gave it the following window procedure:
INT_PTR CALLBACK MaskedEditProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam,
UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (message)
{
case WM_CUT:
return 0;
case WM_PASTE:
return 0;
case WM_KEYDOWN:
if (wParam == VK_DELETE)
{
WORD caretPosition{ LOWORD(SendMessage(hwndDlg,EM_GETSEL,0,0)) };
switch (caretPosition)
{
case 7:
return 0;
case 1:
case 4:
SendMessage(hwndDlg, EM_SETSEL, caretPosition + 1, caretPosition + 2);
break;
default:
SendMessage(hwndDlg, EM_SETSEL, caretPosition, caretPosition + 1);
}
return DefSubclassProc(hwndDlg, WM_CHAR, '0', 0);
}
case WM_CHAR:
if (wParam == '\b')
{
WORD caretPosition{ LOWORD(SendMessage(hwndDlg,EM_GETSEL,0,0)) };
switch (caretPosition)
{
case 0:
return 0;
case 2:
case 5:
SendMessage(hwndDlg, EM_SETSEL, caretPosition - 2, caretPosition - 1);
break;
default:
SendMessage(hwndDlg, EM_SETSEL, caretPosition - 1, caretPosition);
}
return DefSubclassProc(hwndDlg, WM_CHAR, '0', 0);
}
else if (iswdigit(wParam))
{
WORD caretPosition{ LOWORD(SendMessage(hwndDlg,EM_GETSEL,0,0)) };
switch (caretPosition)
{
case 1:
case 4:
SendMessage(hwndDlg, EM_SETSEL, caretPosition + 1, caretPosition + 2);
break;
default:
SendMessage(hwndDlg, EM_SETSEL, caretPosition, caretPosition + 1);
}
}
}
return DefSubclassProc(hwndDlg, message, wParam, lParam);
}
This seems to work as intended, though my backspace key has always been broken, so I haven't tested that part. The control is also set not to accept non-digit input, so I don't think the fact that I don't handle those here should break anything.
I suggest you handle only WM_CHAR (and possibly custom messages related to your masking). Leave the arrow keys alone, let the edit control handle them and in your WM_CHAR handler query for the caret placement. In your WM_CHAR when the placement would normally reach a colon send a EM_SETSEL that skips to the next character location.

Windows 10 - WinAPI - detect if power button pressed [duplicate]

I have a headless computer running a custom service that I want to enable/disable using the power button, rather than having to remotely connect every time. The computer does other things as well, so turning it off is not an option.
Is it possible to hook the system power button under Windows XP & up, such that my program would get the event before Windows initiates a powerdown/sleep event (before PBT_APMQUERYSUSPEND event get sent out)?
This is indeed possible, but it's a bit hackish and requires two completely different implementations depending on the Windows version. For both methods, you need to set your power button to put the computer to sleep in your power options.
Windows XP and below:
You need to overwrite your program's main window's WndProc function. On IDEs that don't support this natively, this can be done using SetWindowLong in user32 API. In your custom WndProc function, listen for a WM_POWERBROADCAST (0x218) message. If you receive a message with wParam of PBT_APMQUERYSUSPEND (0x0), call your wanted function and then return BROADCAST_QUERY_DENY (0x424D5144) instead of calling the base WndProc function. Example code:
//At program start
//GWL_WNDPROC = -4
oldWndProc = SetWindowLong(this.hWnd, GWL_WNDPROC, &MyWndProc)
//In MyWndProc(hWnd, wMsg, wParam, lParam)
//WM_POWERBROADCAST = 0x218
//PBT_APMQUERYSUSPEND = 0x0
//BROADCAST_QUERY_DENY = 0x424D5144
if wMsg = WM_POWERBROADCAST && wParam = PBT_APMQUERYSUSPEND (
//CALL YOUR FUNCTION HERE!
return BROADCAST_QUERY_DENY
)
return CallWindowProc(oldWndProc, hWnd, wMsg, wParam, lParam)
//Before exiting
SetWindowLong(Me.hWnd, GWL_WNDPROC, oldWndProc)
Windows Vista & up: (thanks to Remy Lebeau for setting me on the right track)
You need to override WndProc like for XP, but also call SetThreadExecutionState in kernel32 API to disable sleep mode and RegisterPowerSettingNotification in user32 API to listen for advanced power notifications. You will be listening, in particular, to the GUID_SYSTEM_AWAYMODE notification, which gets sent out when the system was asked to go to sleep but is unable to do so. To easily convert a string to a properly formed LPCGUID you can use UuidFromStringA in rpcrt4.dll API. Example code:
typedef struct UUID{
int d1, d2, d3, d4
} LPCGUID;
//At program start
//GWL_WNDPROC = -4
//ES_CONTINUOUS = 0x80000000
//ES_SYSTEM_REQUIRED = 0x1
//ES_AWAYMODE_REQUIRED = 0x40
//GUID_SYSTEM_AWAYMODE = "98a7f580-01f7-48aa-9c0f-44352c29e5C0"
LPCGUID uid;
oldWndProc = SetWindowLong(this.hWnd, GWL_WNDPROC, &MyWndProc)
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_AWAYMODE_REQUIRED)
UuidFromStringA(*(GUID_SYSTEM_AWAYMODE), uid)
ps = RegisterPowerSettingNotification(this.hWnd, uid, 0)
//In MyWndProc(hWnd, wMsg, wParam, lParam)
//WM_POWERBROADCAST = 0x218
//PBT_POWERSETTINGCHANGE = 0x8013
if wMsg = WM_POWERBROADCAST && wParam = PBT_POWERSETTINGCHANGE (
//CALL YOUR FUNCTION HERE!
//You can additionally extract data from the lParam to verify
//this is the notification you're waiting for (see below)
)
return CallWindowProc(oldWndProc, hWnd, wMsg, wParam, lParam)
//Before exiting
SetWindowLong(Me.hWnd, GWL_WNDPROC, oldWndProc)
UnregisterPowerSettingNotification(ps)
This method has the side effect of turning off your physical screen (not a problem on a headless machine), and also possibly locking your session. Make sure you disable prompting for password after sleeping to avoid this. There's some additional useful information on RegisterPowerSettingNotification available here which shows how to extract information from the lParam in your WndProc function in case you want additional info on the notification. Have fun ;)

Keystrokes sending incorrect identifier in Win32

So I am working on making a calculator with a GUI for a class project and I am currently working on setting up my input detection so I can filter out what keystokes I want to be included in user input and which I want to exclude. I have been debugging it along the way which has been going smoothly up until about 10 complies ago when the wParam value that gets passed in switched from being a Hex value to being a Dec value. I didn't make any significant changes between when it was sending the code in Hex and now as it sends it in Dec so I can't figure out why it changed, in fact I believe it changed after I finished writing a comment to myself in one of the functions I am filtering the message with so I'm pretty confused as to why it changed. The relevant code is posted below (I can't just undo the changes I made because at some point my IDE closed), if someone could tell me why it changed, how to change it back, and if I'm even filtering keystrokes the correct way that would be much appreciated.
The ShiftPressed variable is stored in the class that is using this WinProc method and is accessible to all of these functions. All the code below was working with the wParam being sent in Hex at one point but after I finished the longer comment in the CheckKeyForOp() method, the wParam started being sent in Dec.
LRESULT CALLBACK EditBoxClass::WinProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_KEYUP:
if( wParam == VK_SHIFT )
ShiftPressed = FALSE;
return 0;
case WM_KEYDOWN:
if( wParam == VK_SHIFT )
{
ShiftPressed = TRUE;
return 1;
}
else if( CheckKeyForNum( wParam ) )
return 1;
else if( CheckKeyForOp( wParam ) )
return 1;
else
return 0;
...
}
BOOL EditBoxClass::CheckKeyForNum( WPARAM wParam )
{
switch( wParam )
{
case 0x30: case VK_NUMPAD0:
case 0x31: case VK_NUMPAD1:
case 0x32: case VK_NUMPAD2:
case 0x33: case VK_NUMPAD3:
case 0x34: case VK_NUMPAD4:
case 0x35: case VK_NUMPAD5:
case 0x36: case VK_NUMPAD6:
case 0x37: case VK_NUMPAD7:
case 0x38: case VK_NUMPAD8:
case 0x39: case VK_NUMPAD9:
default: return FALSE;
}
}
BOOL EditBoxClass::CheckKeyForOp( WPARAM wParam )
{
switch( wParam )
{
case VK_OEM_2: // For both of these keys, VK_OEM_2: "/ and ?" and VL_OEM_MINUS: "- and _" the
case VK_OEM_MINUS: // regular keystroke can be used in the calculator but the "second version"
if( ShiftPressed == TRUE ) return FALSE; // denoted by holding the shift key during the keystroke, cannot be used; so filter.
case VK_OEM_PLUS: // This key acts in the same way the VK_OEM_MINUS key does, having both "+/=" register under the
// VK code VK_OEM_PLUS, but both are operators used by the calculator so allow both
case VK_ADD: case VK_SUBTRACT:
case VK_MULTIPLY: case VK_DIVIDE:
case VK_DECIMAL: case VK_OEM_PERIOD: return TRUE;
default: return FALSE;
}
}
Messages do not give you hex/decimal, they just give you binary numbers, which you can then interpret as hex/decimal as needed.
The only error I see in this code is that CheckKeyForNum() is not returning TRUE for allowed number keys:
BOOL EditBoxClass::CheckKeyForNum( WPARAM wParam )
{
switch( wParam )
{
case 0x30: case VK_NUMPAD0:
case 0x31: case VK_NUMPAD1:
case 0x32: case VK_NUMPAD2:
case 0x33: case VK_NUMPAD3:
case 0x34: case VK_NUMPAD4:
case 0x35: case VK_NUMPAD5:
case 0x36: case VK_NUMPAD6:
case 0x37: case VK_NUMPAD7:
case 0x38: case VK_NUMPAD8:
case 0x39: case VK_NUMPAD9:
return TRUE; // <-- add this
default: return FALSE;
}
}

Expanding a button width on click

I'm using checkbox buttons and I want to make the button expand in width when I click it using LBUTTONDOWN
Would this involve using something like AppendMenu()? If so, how would I do it?
This is what I have so far for my button proc:
LRESULT CALLBACK ButtonWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){
static int x,y,btnwidth, btnheight;
switch (message){
case WM_USER:
btnwidth=wParam;
btnheight=lParam;
return 0;
case WM_LBUTTONDOWN:
btnwidth *= 2;
break;
}
You can expand the button by calling MoveWindow. You probably will want to first call GetWindowRect to get the current size and position, and then ScreenToClient to convert the rect to client coordinates. Then you can adjust the client coordinates as you wish and pass them to MoveWindow.

Why can my property sheet, shown from a system tray icon, lock up the taskbar?

Note: code samples have been simplified, but the overall structure remains intact.
I am working on a Win32 application whose main interface is a system tray icon. I create a dummy window, using HWND_MESSAGE as its parent, to receive the icon's messages:
WNDCLASSEX wndClass;
wndClass.lpfnWndProc = &iconWindowProc;
// ...
iconWindowHandle = CreateWindow(wndClass.lpszClassName, _T(""), 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_MESSAGE, NULL, GetModuleHandle(NULL), 0);
Then the icon is created, referring to this message-only window:
NOTIFYICONDATA iconData;
iconData.hWnd = iconWindowHandle;
iconData.uCallbackMessage = TRAYICON_MESSAGE;
// ...
Shell_NotifyIcon(NIM_ADD, &iconData)
When the tray icon is double-clicked, I create and show a property sheet (from comctl32.dll):
LRESULT CALLBACK iconWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case TRAYICON_MESSAGE:
switch (lParam) { // that contains the "real" message
case WM_LBUTTONDBLCLK:
showPropertySheet();
return 0;
// ...
}
break;
// ...
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
The property sheet has no parent window. The PropertySheet function is called from the window procedure of the message-only window. The PSH_MODELESS flag is not set; thus, PropertySheet only returns after the property sheet window is closed again:
void showPropertySheet() {
PROPSHEETPAGE pages[NUM_PAGES];
pages[0].pfnDlgProc = &firstPageDialogProc;
// ...
PROPSHEETHEADER header;
header.hwndParent = NULL;
header.dwFlags = PSH_PROPSHEETPAGE | PSH_USECALLBACK;
header.ppsp = pages;
// ...
PropertySheet(&header);
}
Now all this works just fine, until I set a breakpoint inside the dialog procedure of one of the property sheet's pages:
BOOL CALLBACK firstPageDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
return FALSE; // breakpoint here
}
When the program stops on the breakpoint, the entire taskbar locks up!
The call stack is quite useless; it shows that the dialog procedure is called from somewhere inside comctl32.dll, via some calls inside user32.dll. No window procedure of my own is in between.
Making the property sheet modeless doesn't seem to help. Also, I'd rather not do this because it makes the code more complex.
As long as my dialog procedure returns quickly enough, this shouldn't be a problem. But it seems so weird that a longer operation inside the dialog procedure would not only lock up the dialog itself, but the entire shell. I can imagine that the message-only window procedure has the power to cause this behaviour, since it's more closely related to the tray icon... but this function is not shown on the call stack.
Am I doing something fundamentally wrong? Can anyone shed some light on this issue?
Actually, it's rather obvious, and the confusion must have been due to a lack of coffee.
The taskbar probably uses SendMessage to send the message to my application, which causes it to block until the message is handled. SendMessageTimeout is apparently not used.
I still think it's strange that no function of my own shows up on the call stack. Surely, such a message must flow through my message loop in order to be processed? Maybe the warning that "stack frames below this line may be incomplete or missing" was actually right, then.

Resources