Using WH_JOURNALRECORD and cancel does seem to return the WM_CANCELJOURNAL

我怕爱的太早我们不能终老 提交于 2019-12-24 06:44:36

问题


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 a NULL window handle, therefore it cannot be dispatched to a window procedure. There are two ways for an application to see a WM_CANCELJOURNAL message: If the application is running in its own main loop, it must catch the message between its call to GetMessage or PeekMessage and its call to DispatchMessage. If the application is not running in its own main loop, it must set a GetMsgProc hook procedure (through a call to SetWindowsHookEx specifying the WH_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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!