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);
}
I suppose you’re referring to this section in the documentation:
The problem is that the
WM_CANCELJOURNALmessage is not sent to the callback function you installed withSetWindowsHookEx. But unlike otherWM_*messages, it is also not meant to be processed by a window procedure (WndProcin 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_GETMESSAGEhook: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 theWM_CANCELJOURNALmessage.Update:
In the
GetMessageProccallback function, thecodeparameter 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 constantHC_ACTION. If thecodeparameter is less than 0, the hook procedure should simply call theCallNextHookExfunction without performing any further processing. That’s basically the exact same thing you did for theJournalRecordProccallback function.The window message is going to be found in a
MSGstructure, a pointer to which is passed to the callback function as thelParamparameter. 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 nativeMSGstructure is equivalent to the managedSystem.Windows.Forms.Messagestructure (the same thing used by theWndProcmethod), so if you declare yourGetMessageProccallback function like this, things will be much simpler:Then, the windows message is found as the
Msgmember of theMessagestructure. That’s the value you want to compare againstWM_CANCELJOURNAL:Note that in order for the above call to
CallNextHookExto work, you’ll also have to provide an overloaded definition of theCallNextHookExfunction that matches the signature of yourGetMessageProccallback function: