SetWinEventHook 与 CreateProcess, C++

Posted

技术标签:

【中文标题】SetWinEventHook 与 CreateProcess, C++【英文标题】:SetWinEventHook with CreateProcess, C++ 【发布时间】:2013-12-22 17:31:47 【问题描述】:

我正在使用 CreateProcess 打开一个窗口,但我在理解 SetWinEventHook 时遇到了很多麻烦。

在调用函数中,我有:

HWINEVENTHOOK hook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_CREATE, NULL, WinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT );

BOOL result = CreateProcess(0, arguments,
                    NULL, NULL, FALSE, 0, NULL,
                    NULL, &StartupInfo, &ProcessInfo)

if (hook) 
    UnhookWinEvent(hook);

创建过程顺利进行,但未调用链接到 SetWinEventHook 的 WinEventProc 函数。为了让 WinEventProc 被调用,我尝试过这样的事情:

MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0);

在 createProcess 调用之后,但我不知道如何结束 while 循环,因此它会继续进行。

我已经阅读了很多内容,但我不明白如何使用 SetWinEventHook 来捕获由 CreateProcess 启动的进程。任何帮助表示赞赏!

【问题讨论】:

【参考方案1】:

您可能需要完整且有效的事件循环 - 尝试:

MSG msg;
while(GetMessage(&msg, NULL, 0, 0) > 0)

  TranslateMessage(&msg);
  DispatchMessage(&msg);

如果您的应用程序不是 GUI 应用程序 - 或者更具体地说,如果您不需要除挂钩以外的任何事件循环 - 您可以添加另一个线程并在那里使用上面的事件循环(以及所有挂钩和取消挂钩)我想),或者将PeekMessageGetMessage 结合使用来创建非阻塞事件循环并定期调用它。

其次,你不应该在调用CreateProcess 之后直接移除你的钩子。窗口的创建通常发生在程序完全加载和初始化之后,这个过程可能需要一段时间。 CreateProcess 是异步工作的,这意味着它不会等到这一切都发生后才退出。

第三,为了能够接收任何类型的钩子消息,您的消息循环需要在 SetWinEventHookUnhookWinEvent 之间执行 - 在任何其他情况下,您将一无所获。

最后,为了避免来自其他进程的消息传递给您的进程,您可能应该使用 CREATE_SUSPENDED 标志启动您的应用程序,使用适当的进程 ID 启动您的钩子,然后使用 ResumeThread 和 main 恢复进程执行线程句柄从CreateProcess获取。

总体来说应该是这样的:

BOOL result = CreateProcess(0, arguments,
                NULL, NULL, FALSE, CREATE_SUSPENDED, NULL,
                NULL, &StartupInfo, &ProcessInfo);

HWINEVENTHOOK hook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_CREATE, NULL, WinEventProc, ProcessInfo.dwProcessId, 0, WINEVENT_OUTOFCONTEXT );

ResumeThread(ProcessInfo.hThread);

// If you don't have an event loop function in your application already, then you could insert a temporary one here.
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))

   TranslateMessage(&msg);
   DispatchMessage(&msg);

如果你的应用程序不是事件驱动的——只是常规的顺序应用程序,那么你应该定期调用上面的事件循环,直到你的WinEventProc 执行——你可能应该添加一些安全变量来查看它是否已经被执行。另一个好主意可能是包括一些超时(2-3 分钟?),以防没有发布任何事件(应用程序崩溃,应用程序由于某种原因没有创建对象)。为简单起见,我也跳过了任何错误检查。

你的钩子程序应该做这样的事情:

void CALLBACK WinEventProc(
    HWINEVENTHOOK hWinEventHook,
    DWORD event,
    HWND hwnd,
    LONG idObject,
    LONG idChild,
    DWORD dwEventThread,
    DWORD dwmsEventTime)

    // This will handle the re-entrance problem nicely.
    UnhookWinEvent(hWinEventHook);

    // Do wathever you need to do with a window here.
    [...]


编辑:

至于跳过消息循环 - 是的,这就是这个消息循环应该做的。

哪种消息循环最适合您取决于您​​的程序构造 - 如果您已经运行了偶数循环(Qt、MFC 等 GUI 框架...通常已经为您实现),那么您不需要无论如何添加。如果您希望您的应用程序等到 WinEvent 交付,那么您应该这样做:

//Global variable for stop-condition:
bool docontinue = false;

void CALLBACK WinEventProc(
    HWINEVENTHOOK hWinEventHook,
    DWORD event,
    HWND hwnd,
    LONG idObject,
    LONG idChild,
    DWORD dwEventThread,
    DWORD dwmsEventTime)

    // This will handle the re-entrance problem nicely.
    UnhookWinEvent(hWinEventHook);

    // Do wathever you need to do with a window here.
    [...]
    docontinue = false;

挂钩功能应该这样做:

// In hooking function use:
docontinue = true;
MSG msg;
while (GetMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0)

   TranslateMessage(&msg);
   DispatchMessage(&msg);
   if (!docontinue)
       break;

这是最幼稚的方法,它有几个缺点,但如果你只需要做一个简单的操作,那么它可能对你来说就足够了。

【讨论】:

非常感谢你写了这么多。我将开始查看 PeekMessage 函数以了解更多信息。同时,我将 CreateProcess 移到了 setWinEventHook 调用之上,添加了恢复线程和 while(peekMessage) 循环。代码只是直接跳过了 while 循环,甚至没有碰到 WinEventProc 函数。在 SetWinEventHook 中,我必须将 ProcessInfo.process 转换为类型 DWORD 才能编译,我不知道会有多大的不同。 对不起,我的错 - SetWinEventHook 需要进程 ID 而不是进程句柄 - 你应该使用 dwProcessId 字段而不是 hProcess - 那么你不应该需要转换任何东西 ;)跨度> 而不是docontinue = false; 使用PosteQuitMessage() @milevyo 我相信我的方法更安全。在已经有事件循环的线程中使用这样的循环是完全可能的。在这种情况下,PostaQuitMessage() 将退出线程中的所有消息队列 - 可能与预期不同。

以上是关于SetWinEventHook 与 CreateProcess, C++的主要内容,如果未能解决你的问题,请参考以下文章

csharp 利用SetWinEventHook事件钩子避免SetWindowsHookEx权限问题

SetWinEventHook 事件钩子(有些windows事件并没有消息对应,譬如弹出菜单,切换窗口,获得焦点,滚动条滚动等)good

typescript 解决方案“错误:gulp-typescript:项目不能同时用于两个编译*。使用createP创建多个项目

[原]excel启动时死锁

命令窗口

命令窗口