I am writing a .NET wrapper around Win32 hooks which buffers WM_CHAR messages and allows events such as key presses, key releases, and accelerator keystrokes to be subscribed to. Everything is in working order except apparently my call to TranslateAcce开发者_StackOverflow中文版lerator. It returns true when I expect it to (when it finds an accelerator in the given table which I constructed earlier) but the WM_COMMAND messages don't seem to be showing up ever. Here is some relevant code.
[StructLayout(LayoutKind.Sequential)]
struct MSG {
IntPtr hWnd;
WindowsMessages message;
IntPtr wParam;
IntPtr lParam;
UInt32 time;
POINT pt;
}
delegate IntPtr HOOKPROC(HookCodes nCode, IntPtr wParam, ref MSG lParam);
[DllImport("user32.dll")]
extern IntPtr CallNextHookEx(IntPtr hhk, HookCodes nCode, IntPtr wParam, ref MSG lParam);
IntPtr HookProcedure(HookCodes nCode, IntPtr wParam, ref MSG lParam) {
IntPtr result = IntPtr.Zero;
if(nCode < HookCodes.ACTION) {
result = CallNextHookEx(hHook, nCode, wParam, ref lParam);
} else if(nCode == HookCodes.ACTION && (PeekMessageOptions)wParam == PeekMessageOptions.REMOVE) {
/*
* Under these conditions, each message will only be passed onto the switch below once.
*/
switch(lParam.message) {
case WindowsMessages.KEYDOWN:
//fire keydown events and call TranslateAccelerator/TranslateMessage
break;
case WindowsMessages.KEYUP:
//fire keyup events
break;
case WindowsMessages.COMMAND:
//fire accelerator events Ex: Ctrl+F, ALT+M, SHIFT+L
break;
case WindowsMessages.CHAR:
//place char in buffer
break;
default:
break;
}
}
return result; //Will be zero if action was taken on the message by this procedure.
}
}
In KeyPressed(MSG), messages are translated like so:
[DllImport("user32.dll")]
extern bool TranslateAccelerator(IntPtr hWnd, IntPtr hAccTable, ref MSG lpMsg);
[DllImport("user32.dll")]
extern bool TranslateMessage(ref MSG lpMsg);
if(!TranslateAccelerator(hWnd, hAccel, ref msg)){
TranslateMessage(ref msg);
}
The hook and accelerator table are created like this:
[StructLayout(LayoutKind.Sequential)]
struct ACCEL {
byte fVirt;
ushort key;
ushort cmd;
}
[DllImport("user32.dll")]
extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr lpdwProcessId);
[DllImport("user32.dll")]
extern IntPtr SetWindowsHookEx(WindowsHooks hook, HookProcedure lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll")]
extern IntPtr CreateAcceleratorTable(ACCEL[] lpaccl, int cEntries);
HOOKPROC proc = HookProcedure;
uint pid = GetWindowThreadProcessId(/*IntPtr*/hWnd, IntPtr.Zero);
IntPtr hHook = SetWindowsHookEx(WindowsHooks.GETMESSAGE, proc, IntPtr.Zero, pid);
IntPtr hAccel = CreateAcceleratorTable(/*ACCEL[]*/accelerators, accelerators.Length);
Everything works fine (such as attaching events to key-downs and buffering WM_CHAR messages) except I can't find WM_COMMAND or WM_SYSCOMMAND messages anywhere in the queue. They just don't seem to be visible to my hook procedure, even though TranslateAccelerator returns true when I expect it to. Note that I am really pretty clueless when it comes to Win32 so I'm probably missing something fairly obvious to the trained eye. But I am at my wits end. I've tried attaching the hook to an XNA window and a Windows Form without success.
You won't catch those WM_COMMAND
messages with a GETMESSAGE
hook.
TranslateAccelerator
"sends the WM_COMMAND or WM_SYSCOMMAND message directly to the specified window procedure" (see the documentation) and hence bypasses the message queue.
You'll need a CALLWNDPROC
hook instead.
精彩评论