Suppressed keyboard messages don't die - winapi

I regularly use a certain application (which is closed-sourced, single-threaded, 32-bit, native) that predates such niceties as configurable key bindings. It does support plug-ins, though, so I'm making one to provide the missing feature.
I'm using SetWindowsHookEx to add a WH_KEYBOARD hook. Some key presses go through unchanged, and others get suppressed and handled manually. For the latter case I'm (as the documentation seems to suggest) just returning 1 and ignoring the output of CallNextHookEx. That works in most cases, but there's at least one case--Ctrl+D--that the application is still handling even though I told it not to.
Is there something more/different I should be doing to squash that keystroke?
Maybe relevant: normally I get both an HC_NOREMOVE and an HC_ACTION version of each message, but for Ctrl+D I only get the HC_ACTION version.
C# source:
private IntPtr ProcessWindowsMessage(int code, IntPtr wParam, IntPtr lParam)
{
if (code < 0 || code == HC_NOREMOVE)
{
return Win32.CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
}
Keys key = GetKeyFromWParam(wParam);
Keys keyCombo = key | GetCurrentModifiers();
var isKeyDownEvent = IsKeyDownEvent(lParam);
var handled = stateMachine.Add(keyCombo, isKeyDownEvent);
if (handled)
{
Win32.CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
return LResultHandled;
}
else
{
return Win32.CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
}
}
private static readonly IntPtr LResultHandled = new IntPtr(1);

Related

Are delay-loaded DLLs useful to avoid linking specific functions?

Consider the following code which needs to call one of two functions in User32.dll.
if( IsWindowsVistaOrGreater() )
AddClipboardFormatListener(hWnd) ;
else
SetClipboardViewer(hWnd) ;
If I'm not mistaken, this program will fail to start on WinXP because AddClipboardFormatListener does not exist in User32.dll under XP.
One way to solve this is not calling AddClipboardFormatListener directly but rather get a pointer to it ourselves:
GetProcAddress(GetModuleHandle("User32.dll"), "AddClipboardFormatListener").
However, if I instruct the linker to delay-load User32.dll...
Would this avoid loading that specific function under XP so that I don't need to call GetModuleHandle and GetProcAddress?
Is it recommended to delay-load a DLL when only a few functions need to be delay-loaded?
The case of User32.dll is particularly dramatic on the second point since most of the functions used in the program are know to exist in that DLL on all Windows versions.
I would guess that linking at load-time is more efficient than at run-time since the latter needs additional checks before each function call.
But I'm just guessing, hence the question.
Would this avoid loading that specific function under XP so that I don't need to call GetModuleHandle and GetProcAddress?
Yes. This is exactly the type of situation that delay-loading was invented for. your code can call a DLL function as if it were statically linked, but the executable will not load the DLL function pointer at runtime until the function is actually called for the first time. Internally, the delay-load mechanism uses LoadLibrary() and GetProcAddress() for you.
Is it recommended to delay-load a DLL when only a few functions need to be delay-loaded?
If a DLL is delay-loaded, ALL of its functions are delay-loaded, you cannot pick and choose which ones you want. So, if your app needs to use a lot of functions from the same DLL, like user32.dll, then static linking is usually more efficient, and then you can use GetProcAddress() manually for the few functions you really need to handle differently.
In this case, I would suggest getting rid of the OS check altogether and rely only on whether the DLL function actually exists or not, eg:
typedef BOOL (WINAPI *LPFN_ACFL)(HWND);
LPFN_ACFL lpAddClipboardFormatListener = (LPFN_ACFL) GetProcAddress(GetModuleHandle(TEXT("user32")), "AddClipboardFormatListener");
if( lpAddClipboardFormatListener != NULL )
lpAddClipboardFormatListener(hWnd);
else
SetClipboardViewer(hWnd);
Where delay-loading really shines is in its hooks. For instance, on earlier systems, you can use a delay-load failure hook to implement your own version of AddClipboardFormatListener(), and then your main code can just call AddClipboardFormatListener() unconditionally on all systems and it won't know the difference. For example (just a demo, not actually tested):
LRESULT CALLBACK ClipboardSubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg)
{
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, &ClipboardSubClassProc, uIdSubclass);
break;
case WM_CHANGECBCHAIN:
{
if (wParam == dwRefData)
SetWindowSubclass(hWnd, &ClipboardSubClassProc, uIdSubclass, lParam);
else if (dwRefData != 0)
SendMessage((HWND)dwRefData, uMsg, wParam, lParam);
break;
}
case WM_DRAWCLIPBOARD:
{
SendMessage(hWnd, WM_CLIPBOARDUPDATE, 0, 0);
if (dwRefData != 0)
SendMessage((HWND)dwRefData, uMsg, wParam, lParam);
break;
}
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
BOOL WINAPI My_AddClipboardFormatListener(HWND hWnd)
{
HWND hWndNext = SetClipboardViewer(hWnd);
if ((!hWndNext) && (GetLastError() != 0))
return FALSE;
if (!SetWindowSubclass(hWnd, &ClipboardSubClassProc, 1, (DWORD_PTR)hWndNext))
{
DWORD dwErr = GetLastError();
ChangeClipboardChain(hWnd, hwndNext);
SetLastError(dwErr);
return FALSE;
}
return TRUE;
}
BOOL WINAPI My_RemoveClipboardFormatListener(HWND hWnd)
{
DWORD_PTR dwRefData;
if (!GetWindowSubclass(hWnd, &ClipboardSubClassProc, 1, &dwRefData))
{
SetLastError(ERROR_NOT_FOUND);
return FALSE;
}
RemoveWindowSubclass(hWnd, &ClipboardSubClassProc, 1);
return ChangeClipboardChain(hWnd, (HWND)dwRefData);
}
FARPROC WINAPI MyDliFailureHook(unsigned dliNotify, PDelayLoadInfo pdli)
{
if ((dliNotify == dliFailGetProc) && (pdli->dlp.fImportByName))
{
if (strcmp(pdli->dlp.szProcName, "AddClipboardFormatListener") == 0)
return (FARPROC) &My_AddClipboardFormatListener;
if (strcmp(pdli->dlp.szProcName, "RemoveClipboardFormatListener") == 0)
return (FARPROC) &My_RemoveClipboardFormatListener;
}
return NULL;
}
__pfnDliFailureHook2 = &MyDliFailureHook;
...
LRESULT CALLBACK MyWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
AddClipboardFormatListener(hWnd);
break;
case WM_DESTROY:
RemoveClipboardFormatListener(hWnd);
break;
case WM_CLIPBOARDUPDATE:
// do all of your clipboard processing here...
break;
...
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

How to set text for Static in subclass proc?

I'm trying to change text of a Static control in another application.
I'm not able to change its text by using WM_SETTEXT from my program. (The target app has some mechanisms to prevent this)
So I inject a library to the target app. In the library I subclass the Static control.
WNDPROC wndProcOrig;
LRESULT CALLBACK SubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_SETTEXT:
DefWindowProc(hWnd, uMsg, wParam, (LPARAM)"1234");
return TRUE;
case WM_USER + 5003:
return TRUE;
}
return CallWindowProc(wndProcOrig, hWnd, uMsg, wParam, lParam);
}
wndProcOrig = (WNDPROC)SetWindowLongPtr(textHandle, GWLP_WNDPROC, (LONG_PTR)SubclassWndProc);
I use the Window Detective to set the text to the Static, when I'm debugging I can the program breaks in SubclassWndProc.
What I have observed:
The SubclassWndProc get called.
The string of lParam is always empty (the value is modified by target app)
After the function called, the text is still empty (not '1234')
Questions:
How does the target app modify the message value?
Is 'DefWindowProc' the most underlying API programmer can use in order to change text of a Static control? If not, what's the best way to change text of a Static control? (Tried SetWindowText, SetDlgItemText, SendMessage with WM_SETTEXT, neither works)
Yes DefWindowProc is basically the lowest level but a window does not have to use it, it can answer WM_GETTEXT directly instead.
Try passing WM_GETTEXT and WM_SETTEXT directly to DefWindowProc.

Encapsulate Win32 in a class - pass class pointer in CreateWindow

I viewed many tutorials and posts on this subject, and they all say I pass the class instance pointer (this) in my CreateWindowEx() function, and then store it in the window procedure function, when the WM_NCCREATE message is sent. I guess this is because WM_NCCREATE is supposedly the first message that gets sent to the window procedure since a window is created.
A few questions/notes:
From debugging I came to know that actually WM_GETMINMAXINFO is the first sent message, before WM_NCCREATE (at least on my machine). Does this mean I should listen for this message instead of WM_NCCREATE?
According to this popular article, the reason everyone calls SetWindowLongPtr() after the message WM_NCCREATE is received is because
If the value does not exist by the time WM_NCCREATE is called then,
by some mysterious Windows behavior that I still don't understand, the
value never gets inserted.
I tried to do exactly that (that is, call SetWindowLongPtr() after CreateWindowEx()). It turns out to be just fine. The application runs okay. Below is my code, please tell me if there's something wrong with this approach.
void GLWin32::CreateWindow(...)
{
...
_hwnd = CreateWindowEx(NULL, _wndclassex.lpszClassName, title.c_str(), WS_OVERLAPPEDWINDOW, x, y, width, height, NULL, NULL, _hinstance, NULL);
SetWindowLongPtr(_hwnd, GWL_USERDATA, reinterpret_cast<LONG_PTR>(this));
...
}
//static window procedure for all instances of this class
LRESULT CALLBACK GLWin32::_msgRouter(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
LONG l = GetWindowLongPtr(hwnd, GWLP_USERDATA);
GLWin32* thisPtr = reinterpret_cast<GLWin32*>(l);
if (thisPtr)
return thisPtr->_winProc(msg, wparam, lparam);
else
return DefWindowProc(hwnd, msg, wparam, lparam);
}
LRESULT GLWin32::_winProc(UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_CLOSE:
{
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(_hwnd, msg, wparam, lparam);
}
Why is my approach not used instead of the popular approach?
The problem with this approach is that if you want to use the instance when processing any of the window creation messages (including "ordinary" messages that are sent as part of the creation process), you won't have access to it.
Suppose you want to create a button when processing WM_CREATE (typical scenario) and you want to set the button text to some instance member value. You want something like:
LRESULT GLWin32::_winProc(UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_CREATE:
{
CreateWindow("BUTTON", this->buttonText, WS_VISIBLE | WS_CHILD,
10, 10, 50, 30, this->hwnd, NULL, this->hInstance, NULL);
return 0;
}
}
return DefWindowProc(_hwnd, msg, wparam, lparam);
}
Problem is, when WM_CREATE is processed (before CreateWindowEx returns), SetWindowLongPtr wasn't called yet and the instance pointer is missing, so _winProc isn't being called at all.

Using KBDLLHOOKSTRUCT to determine first key press

I'm using a low-level keyboard hook on windows. It works like a charme, despite of the fact that I'm currently unable to tell whether the key was initially pressed or pressed again. The documentation (+here) says, that bit 7 holds the transition state. But this seems only to be true when the key is being released. Bit 7 is sadly not set when I firstly press the key.
Is there any way to tell whether the key is pressed initially?
I happen to run into this problem recently. I can't find any good solutions, but I ended up using a flag and a GetAsyncKeyState before SetWindowHookEx.
BOOL wasDown;
LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode == HC_ACTION) {
LPKBDLLHOOKSTRUCT key = (LPKBDLLHOOKSTRUCT) lParam;
if (key->vkCode == VK_SOMETHING) {
switch (wParam) {
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
if (!wasDown) {
// Processing on first key down
wasDown = true;
}
break;
case WM_KEYUP:
case WM_SYSKEYUP:
// Processing on key up
wasDown = FALSE;
break;
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
wasDown = GetAsyncKeyState(VK_SOMETHING) < 0;
hHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, hInstance, 0);
Of course, this code only does it for one key. You can use an array of flags to do multiple keys. Depending on your application you could also unconditionally set the flag to false if you want the first press after your hook is set.

SetWindowsHookEx to block 64-bit WM_HOTKEY from 32-bit

Our application requires running in a "secure kiosk mode" on Windows. We have a variety of ways we block various actions. One thing that we do is listen for the use of hotkeys using SetWindowsHookEx to register a WH_GETMESSAGE hook, then when a WM_HOTKEY message comes through we change it to WM_NULL (see code below). This works great in most cases but we recently uncovered an issue. On a 64-bit machine, if the application listening and responding to the hotkey is a 64-bit application we cannot block it with our 32-bit application.
We're trying to find ways to solve this and the only option I can think of is spawning a background 64-bit process to handle this hook for 64-bit applications. Are there other (more simple) alternatives?
Setting up the hook:
HHOOK getMessageHook = SetWindowsHookEx(WH_GETMESSAGE, GetMessageProc, hInst, 0);
GetMsgProc:
LRESULT CALLBACK GetMessageProc(int nCode, WPARAM wParam, LPARAM lParam)
{
switch (nCode)
{
case HC_ACTION:
{
MSG *msg = (MSG *) lParam;
if (msg->message == WM_HOTKEY)
{
// A hotkey was pressed, changed the message to WM_NULL
msg->message = WM_NULL;
}
break;
}
default:
break;
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}

Resources