使用 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#,并且我已经让程序成功记录了使用 SetWindowsHookEx
和 WH_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
消息:如果应用程序在其自己的主循环中运行,它必须在调用GetMessage
或PeekMessage
和调用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 挂钩 - 从未调用过回调。