问题
I'm using C# and I've got the program successfully recording the journal messages using SetWindowsHookEx
with WH_JOURNALRECORD
.
My problem comes when it's time to stop. The docs show that if the user pressed CTRL-ESC or CTRL-ALT-DELETE a WM_CANCELJOURNAL
message will be posted that I can watch to know when to stop. My application gets unhooked but I never seem to get a WM_CANCELJOURNAL
.
I have two hooks setup. One hook to do the Journal Record and one to check for the cancel message:
IntPtr hinstance = Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]);
JournalRecordProcedure = JournalRecordProc;
journalHook = SetWindowsHookEx(WH_JOURNALRECORD, JournalRecordProcedure, hinstance, 0);
GetMessageProcedure = GetMessageProc;
messageHook = SetWindowsHookEx(WH_GETMESSAGE, GetMessageProcedure, hinstance, 0);
------
public static int JournalRecordProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode < 0) return CallNextHookEx(journalHook, nCode, wParam, lParam);
EventMsgStruct msg = (EventMsgStruct) Marshal.PtrToStructure(lParam, typeof (EventMsgStruct));
script.Add(msg); //just a quick way to record for now
return CallNextHookEx(journalHook, nCode, wParam, lParam);
}
public static int GetMessageProc(int code, IntPtr wParam, IntPtr lParam)
{
//it comes here but how do I test if it's WM_CANCELJOURNAL ??
//code always seems to be equal to zero.. I must be missing something
return CallNextHookEx(journalHook, code, wParam, lParam);
}
回答1:
I suppose you're referring to this section in the documentation:
This role as a signal to stop journal recording means that a CTRL+BREAK key combination cannot itself be recorded. Since the CTRL+C key combination has no such role as a journaling signal, it can be recorded. There are two other key combinations that cannot be recorded: CTRL+ESC and CTRL+ALT+DEL. Those two key combinations cause the system to stop all journaling activities (record or playback), remove all journaling hooks, and post a
WM_CANCELJOURNAL
message to the journaling application.
The problem is that the WM_CANCELJOURNAL message is not sent to the callback function you installed with SetWindowsHookEx
. But unlike other WM_*
messages, it is also not meant to be processed by a window procedure (WndProc
in WinForms) because it is posted to the message queue of the thread and is not associated with any particular window.
Rather, the documentation advises that one must process it within an application's main loop or using a WH_GETMESSAGE hook:
This message does not return a value. It is meant to be processed from within an application's main loop or a GetMessage hook procedure, not from a window procedure.
[ . . . ]
The
WM_CANCELJOURNAL
message has aNULL
window handle, therefore it cannot be dispatched to a window procedure. There are two ways for an application to see aWM_CANCELJOURNAL
message: If the application is running in its own main loop, it must catch the message between its call toGetMessage
orPeekMessage
and its call toDispatchMessage
. If the application is not running in its own main loop, it must set aGetMsgProc
hook procedure (through a call toSetWindowsHookEx
specifying theWH_GETMESSAGE
hook type) that watches for the message.
In managed WinForms code, you obviously don't have any access to or control over the application's main loop. I'm not sure if adding a message filter to your application will let you handle this message or not: I haven't tried it. If it will, that's probably the route you want to take, considering the alternative, which is to install a second hook, WH_GETMESSAGE
, and then in that hook procedure, listen for the WM_CANCELJOURNAL
message.
Update:
In the GetMessageProc callback function, the code
parameter just tells you whether the hook procedure should process the message. Virtually all of the time, it's going to be 0, which is equivalent to the symbolic constant HC_ACTION
. If the code
parameter is less than 0, the hook procedure should simply call the CallNextHookEx
function without performing any further processing. That's basically the exact same thing you did for the JournalRecordProc
callback function.
The window message is going to be found in a MSG structure, a pointer to which is passed to the callback function as the lParam
parameter. But that's Win32 stuff. Don't mess with raw pointers in .NET, let the P/Invoke marshaler handle all of that dirty stuff for you. The native MSG
structure is equivalent to the managed System.Windows.Forms.Message structure (the same thing used by the WndProc
method), so if you declare your GetMessageProc
callback function like this, things will be much simpler:
public delegate int GetMessageProc(int code, IntPtr wParam, ref Message lParam);
Then, the windows message is found as the Msg member of the Message
structure. That's the value you want to compare against WM_CANCELJOURNAL
:
public static int GetMessageProc(int code, IntPtr wParam, ref Message lParam)
{
if (code >= 0)
{
if (lParam.Msg == WM_CANCELJOURNAL)
{
// do something
}
}
return CallNextHookEx(messageHook, code, wParam, ref lParam);
}
Note that in order for the above call to CallNextHookEx
to work, you'll also have to provide an overloaded definition of the CallNextHookEx
function that matches the signature of your GetMessageProc
callback function:
[DllImport("user32.dll")]
public static extern int CallNextHookEx(IntPtr hHook, int nCode,
IntPtr wParam, ref Message lParam);
来源:https://stackoverflow.com/questions/9317123/using-wh-journalrecord-and-cancel-does-seem-to-return-the-wm-canceljournal