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);
}
Related
I've written a program which wires up a keyboard hook to listen out for F12 to be pressed, on receipt of F12 being pressed I fire an event to start a separate process in my main program.
This works beautifully on my host computer and and while any other app on the machine has focus, but the keyboard hook stops working when a full screen RDP session is focused or virtual machine running in VMWare is focused.
I use SetWindowsHookEx to set up the keyboard hook as follows:
public static int KeyboardHook;
public void AwaitKeyboard(IntPtr handle)
{
_keyboardHookProcedure = new HookProc(KeyboardHookProc);
KeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, _keyboardHookProcedure, handle, 0);
}
Then inside the KeyboardHookProc method I can add code to do something with the main window
public static int KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode < 0)
{
return CallNextHookEx(KeyboardHook, nCode, wParam, lParam);
}
MainWindow mainWindow = (MainWindow)Application.Current.MainWindow;
if (mainWindow != null)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
if (vkCode == mainWindow.KeyboardShortcutCode) // Keyboard shortcut pressed
{
mainWindow.KeyboardShortcutPressed();
}
}
return CallNextHookEx(KeyboardHook, nCode, wParam, lParam);
}
else
{
return CallNextHookEx(KeyboardHook, nCode, wParam, lParam);
}
}
This all works fine for apps on my host machine. But the moment a VM or full screen RDP window is activated, the KeyboardHookProc method is never invoked.
I've read suggestions that I could clear the hook and re-set it whenever the active window changes in Windows. I have tried that and got that working, but re-setting up the hook didn't fix the problem as KeyboardHookProc is still not invoked when in a VM or RDP.
The moment I return to the host machine and press F12 my keyboard hook springs to life and operates as I need it to.
Does anyone have any suggestions?
Thanks
Most likely a kernel driver or something before you in the hook chain is eating the key. This is probably by design as far as those other products are concerned.
I see the same issue on my machine, VirtualBox is eating my Foobar2000 hotkeys when it has focus.
WH_KEYBOARD_LL is pretty much as low as you can go in usermode. You could try raw input but I doubt that is going to work.
I use SetWindowsHookEx to install a global GETMESSAGE hook in a dll like this:
SetWindowsHookEx(WH_GETMESSAGE,HookProc,hModule,0);
and the the hook procedure within the dll too.But when i called InstallHook(this function is exported from the DLL) to install the GETMESSAGE hook,booom, the system can not respon the mouse operation,the windows on the desktop can be close or move) ,here is my HookProc Code:
LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode < 0)
{
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
WCHAR szMsg[MAX_PATH] = { 0 };
PMSG pMsg = (PMSG)lParam;
wsprintf(szMsg, L"MSG_FROM:%08x,MSG_TYPE:%d", pMsg->hwnd,pMsg->message);
OutputDebugString(szMsg);
return CallNextHookEx(NULL,nCode, wParam, lParam);
}
I've read the MSDN for many times,it just said i must process the message if the ncode is HC_ACTION,but,i don't know how to process the message.I think there must be someone else has this problem too, so, hope somebody give some help, and tell me how you process this trouble.
Thanks a lot.
I asked a related question earlier and realised that not calling ValidateRect in the application as response to WM_PAINT causes tremendous slowdown.
Why is this? How could this affect a DirectX application in such a manner?
// ----------------------------------------------------------------------------
// Name: MsgProc()
// Desc: The window's message handler
//-----------------------------------------------------------------------------
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) {
switch(msg) {
case WM_DESTROY:
Cleanup();
PostQuitMessage( 0 );
return 0;
case WM_PAINT:
Render();
ValidateRect( hWnd, NULL );
return 0;
}
return DefWindowProc( hWnd, msg, wParam, lParam );
}
Your application is slowing down because it is rendering far more than necessary. It is receiving a furious stream of WM_PAINT messages.
Windows sends a WM_PAINT message to a window when it thinks the window should be painted. It makes this determination based on whether a window has been invalidated. Once you have rendered the window completely, you must tell Windows that this is the case by validating the window’s client area. As long as there is some invalidated area, Windows will continue to send your window WM_PAINT messages.
Before DirectX, application windows were validated when they called EndPaint. You can still call BeginPaint and EndPaint, wrapping whatever DirectX rendering you’re doing, or you can simply call ValidateRect when you’re done rendering.
I'm working on a windows application where I'm implementing the whole event loop and everything like that myself (there's a reason for that). In one place, I need to execute some code AFTER a window is shown. Normally, when the window is created, I do some initialisation when WM_CREATE message is received. WM_SHOWWINDOW is sent just BEFORE the window is displayed. However I need to get some code executed right AFTER the window is displayed for the first time. I can't seem to find a notification message sent AFTER the window is shown. Could it be that there isn't one?
Of course, I can keep a boolean - FirstRun - indicating whether or not I had performed my logic, and then execute the code when WM_ACTIVATE is received, provided the boolean is TRUE, then set FirstRun to FALSE so that the code is not execute the next time I am receive WM_ACTIVATE, but this seems somewhat unnatural to me.
It's been ages since I did win32 programming on this level, so can't remember much of it. What is the best approach here?
There is no special notification, but in many cases you can use this trick:
LRESULT CALLBACK MainWndProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam) // second message parameter
{
switch (uMsg)
{
case WM_USER + 100:
//window is just displayed, do some actions
return DefWindowProc(hwnd, uMsg, wParam, lParam);
case WM_CREATE:
PostMessage(hwnd, WM_USER + 100, 0, 0);
return DefWindowProc(hwnd, uMsg, wParam, lParam);
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
There isn't one because that's what WM_SHOWWINDOW is for. Once that mesage is passed to the default message handling procedure, the window will be shown. The best thing you could do is poll with IsWindowVisible via some sort of timer.
Your program design seems flawed though to have to rely on something like this. What are you trying to do?
As long as you are implementing the whole event loop and everything like that yourself, you can handle this directly in WinMain() like this:
HWND hWnd = CreateWindow(...);
if (!hWnd) return 0;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
OnWindowJustDisplayed(); // here
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
#include <stdio.h>
#include <windows.h>
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
printf("Keyboard event, nCode = %d, wParam = %d, lParam = 0x%.8X\n", nCode, wParam, lParam);
return (LRESULT)NULL;
}
void main() {
HHOOK HookHandle = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)KeyboardProc, (HINSTANCE) NULL, GetCurrentThreadId());
printf("Hook handle = 0x%.8X\n", HookHandle);
MSG message;
while (GetMessage(&message,NULL,0,0)) {
TranslateMessage(&message);
DispatchMessage(&message);
}
UnhookWindowsHookEx(HookHandle);
}
Expected it outputs something like "keyboard event...", but it does not work, it always keep silent whatever I type.
The compile options is simple: gcc -o test.exe test.c
Most (if not all) of these hooks have to be injected into the target application(s), so they must be implemented in a DLL.
From the documentation of the KeyboardProc callback function:
The system calls this function
whenever an application calls the
GetMessage or PeekMessage function and
there is a keyboard message (WM_KEYUP
or WM_KEYDOWN) to be processed.
Console applications don't use the message queue to handle input, so your hook will never be called.
You could try using a low-level keyboard hook (WH_KEYBOARD_LL). Even though that's a global hook, you don't need to use a DLL: as mentioned in the Remarks section of the documentation, the hook code is not injected in other processes. The problem with this approach of course is that you'll get notifications for all key events in the system (not just the ones in your program).