使用 WH_JOURNALRECORD 和取消似乎确实返回了 WM_CANCELJOURNAL

Posted

技术标签:

【中文标题】使用 WH_JOURNALRECORD 和取消似乎确实返回了 WM_CANCELJOURNAL【英文标题】:Using WH_JOURNALRECORD and cancel does seem to return the WM_CANCELJOURNAL 【发布时间】:2012-02-16 18:41:12 【问题描述】:

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

到了该停下来的时候,我的问题就来了。文档显示,如果用户按下 CTRL-ESC 或 CTRL-ALT-DELETE,将发布 WM_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);

【问题讨论】:

【参考方案1】:

我想你指的是documentation中的这一部分:

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

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

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

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

[ . . . ]

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

在托管的 WinForms 代码中,您显然无法访问或控制应用程序的主循环。我不确定您的应用程序的adding a message filter 是否会让您处理此消息:我还没有尝试过。如果可以,这可能是您想要采取的路线,考虑替代方案,即安装 second 挂钩 WH_GETMESSAGE,然后在该挂钩过程中,侦听 WM_CANCELJOURNAL 消息.


更新:

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

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

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

然后,windows 消息作为Message 结构的Msg member 找到。这就是您要与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 的调用能够正常工作,您还必须提供与GetMessageProc 回调函数的签名相匹配的CallNextHookEx 函数的重载定义:

[DllImport("user32.dll")]
public static extern int CallNextHookEx(IntPtr hHook, int nCode,
                                        IntPtr wParam, ref Message lParam);

【讨论】:

感谢您的回复。应用程序无法通过 Application 对象安装挂钩,因此我唯一的办法是创建另一个全局挂钩,这是我一直在尝试的方法,但没有成功。 @Kelly:您知道要安装另一个挂钩,您需要再次调用SetWindowsHookEx,这次使用WH_GETMESSAGE 常量,然后指定不同的挂钩过程?用你一直在尝试的代码更新你的问题,我会试着看看你哪里出错了。 我更新了问题以提供更多详细信息。我正在做两个钩子。第一个做日志记录,第二个只是监听取消消息。钩子起作用了,我在 GetMessageProc 中看到了消息,但我从来没有看到任何我可以识别为 WM_CANCELJOURNAL 的东西。任何想法我如何测试它? @Kelly:您正在检查code 的值,但这与您的第一个挂钩回调过程中的nCode 相同。消息作为lParam 中的指针传递的结构的成员传递。对不起,事情变得复杂了。我已经用应该可以工作的代码更新了我的答案。

以上是关于使用 WH_JOURNALRECORD 和取消似乎确实返回了 WM_CANCELJOURNAL的主要内容,如果未能解决你的问题,请参考以下文章

Windows (C++) 中的 WH_JOURNALRECORD 挂钩 - 从未调用过回调。

WH_JOURNALRECORD 的 SetWindowsHookEx 在 Vista/Windows 7 下失败

Apollo GraphQL 取消订阅似乎坏了

后台任务似乎没有被取消/结束

成功的取消引用似乎会导致潜在的段错误

当空引用似乎不可能时,为啥我们会收到可能的取消引用空引用警告?