1

我正在使用 C#,并且我已经让程序使用SetWindowsHookExwith成功记录了日志消息WH_JOURNALRECORD

我的问题在该停下来的时候出现了。文档显示,如果用户按下 CTRL-ESC 或 CTRL-ALT-DELETEWM_CANCELJOURNAL将发布一条消息,我可以查看该消息以了解何时停止。我的应用程序未绑定,但我似乎从未获得WM_CANCELJOURNAL.

我有两个钩子设置。一个用来做日志记录的钩子和一个用来检查取消消息的钩子:

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);
}
4

1 回答 1

1

我想您指的是文档中的此部分:

这个作为停止日志记录信号的作用意味着 CTRL+BREAK 组合键本身不能被记录。由于 CTRL+C 组合键没有日志信号的作用,因此可以记录下来。还有两个无法记录的其他组合键:CTRL+ESC 和 CTRL+ALT+DEL。这两个键组合使系统停止所有日志活动(记录或回放),删除所有日志挂钩,并将WM_CANCELJOURNAL消息发布到日志应用程序。

问题WM_CANCELJOURNAL消息没有发送到您安装的回调函数。但与其他消息不同,它也不意味着由窗口过程(在 WinForms 中)处理,因为它被发布到线程的消息队列中并且不与任何特定窗口相关联。SetWindowsHookExWM_*WndProc

相反,文档建议必须在应用程序的主循环中或使用WH_GETMESSAGE钩子来处理它:

此消息不返回值。它旨在从应用程序的主循环或 GetMessage 挂钩过程中进行处理,而不是从窗口过程中进行处理。

[ . . . ]

WM_CANCELJOURNAL消息有一个NULL窗口句柄,因此它不能被分派给一个窗口过程。应用程序可以通过两种方式查看WM_CANCELJOURNAL消息: 如果应用程序在自己的主循环中运行,它必须在调用GetMessageorPeekMessage和调用之间捕获消息DispatchMessage。如果应用程序没有在自己的主循环中运行,它必须设置一个GetMsgProc挂钩过程(通过调用SetWindowsHookEx指定WH_GETMESSAGE挂钩类型)来监视消息。

在托管的 WinForms 代码中,您显然无法访问或控制应用程序的主循环。我不确定向您的应用程序添加消息过滤器是否可以让您处理此消息:我还没有尝试过。如果是这样,那可能就是您想要采取的路线,考虑替代方案,即安装第二个挂钩,WH_GETMESSAGE然后在该挂钩过程中收听WM_CANCELJOURNAL消息。


更新:

GetMessageProc回调函数中,code参数只是告诉你钩子过程是否应该处理消息。几乎所有时间,它将为 0,相当于符号常量HC_ACTION。如果code参数小于 0,钩子过程应该简单地调用CallNextHookEx函数而不执行任何进一步的处理。JournalRecordProc这基本上与您为回调函数所做的完全相同。

窗口消息将在一个MSG结构中找到,一个指向该结构的指针作为lParam参数传递给回调函数。但那是 Win32 的东西。不要乱用 .NET 中的原始指针,让 P/Invoke 封送处理程序为您处理所有脏东西。本机MSG结构等同于托管System.Windows.Forms.Message结构WndProc方法使用相同的东西),所以如果你GetMessageProc像这样声明你的回调函数,事情会简单得多:

public delegate int GetMessageProc(int code, IntPtr wParam, ref Message lParam);

然后,windows 消息被找到作为结构的Msg成员Message。这就是您要比较的值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);
}

请注意,为了使上述调用CallNextHookEx起作用,您还必须提供与回调函数CallNextHookEx签名匹配的函数的重载定义:GetMessageProc

[DllImport("user32.dll")]
public static extern int CallNextHookEx(IntPtr hHook, int nCode,
                                        IntPtr wParam, ref Message lParam);
于 2012-02-16T18:58:29.457 回答