【问题标题】:How to define the message loop when using SetWinEventHook()?使用 SetWinEventHook() 时如何定义消息循环?
【发布时间】:2021-11-18 15:18:59
【问题描述】:

我正在尝试在第三方应用窗口中收听事件,我不拥有其中的源。

我不明白documentation 的这部分内容:

调用 SetWinEventHook 的客户端线程必须有一个消息循环才能接收事件。

我如何“定义”这个消息循环?

在我的钩子代码中,它永远不会到达switch

void CALLBACK WinEventProc(
    HWINEVENTHOOK hWinEventHook,
    DWORD event,
    HWND hwnd,
    LONG idObject,
    LONG idChild,
    DWORD dwEventThread,
    DWORD dwmsEventTime
)
{   
    switch (event) {
        case EVENT_SYSTEM_MINIMIZESTART:
            ...
            break;
        case EVENT_SYSTEM_MINIMIZEEND:
            ...
            break;
    }
}

HWINEVENTHOOK hWinEventHook;

int EventHook() {

    hWinEventHook = SetWinEventHook(
        EVENT_SYSTEM_MINIMIZESTART,         // eventMin
        EVENT_SYSTEM_MINIMIZEEND,           // eventMax
        NULL,                               // hmodWinEventProc
        WinEventProc,                       // pfnWinEventProc
        4834,                               // idProcess
        0,                                  // idThread
        WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); //dwFlags

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::thread t1(EventHook);
    ...
}

我按照cmets中的建议修改了代码,但是现在我的程序崩溃了;

到达线路时崩溃:

while (GetMessage(&msg, NULL, 0, 0))

【问题讨论】:

  • “消息循环”是及时重复调用GetMessageDispatchMessage的简写。您示例中的 while 循环实际上是一个消息循环。就问题而言,它在别处。你检查SetWinEventHook调用是否成功? 4834是某个进程的有效ID吗?您是否最小化或恢复了属于该进程的某些窗口?
  • @IgorTandetnik 你的意思是如果hWinEventHook 正在返回一个值?确实如此。
  • 你有一个消息循环,但是当你使用 std::async 时它还不够好。要求是消息循环在设置挂钩的同一线程上运行。轻松修复,您根本不需要它是异步的。
  • 您可能还需要为 Windows 而不是控制台进行编译,以便主线程是 UI 线程,并且代码可能应该从主线程或至少是 STA 线程运行。我认为钩子有点难以理解和正确,如果做得不好可能会极大地影响系统的稳定性。除非您别无选择并且愿意大量阅读,否则最好避免使用它们...

标签: c++ winapi hook


【解决方案1】:

消息循环需要在 main 中,在您的情况下,在调用 SetWinEventHook 之后。而且由于您没有其他方式让程序退出,您可能希望创建一个带有退出按钮的对话框,并在该按钮的处理程序中 BN_CLICKED 使用对 MessageBox 的调用来确认用户想要退出如果是这样,请执行 PostQuitMessage)。确认是因为否则太容易退出了。

【讨论】:

  • 我修改了帖子,添加了调试信息,我不明白你的意思需要在main,我的疑问是如何在辅助线程上启动它。
【解决方案2】:

您显示的消息循环非常好(将其移至EventHook() 后)。

“崩溃”并不是真正的崩溃。这只是程序突然终止自身,因为当std::thread 对象被销毁时线程仍在运行,因为在_tmain() 退出时它超出范围之前您还没有调用join()detach()

std::thread::~thread:

销毁线程对象。

如果 *this 有关联的线程 (joinable() == true),则调用 std::terminate()

注意事项

一个线程对象在之后没有关联的线程(并且可以安全地销毁)

  • 它是默认构造的
  • 它被移出
  • join() 已被调用
  • detach() 已被调用

所以,在_tmain() 退出之前简单地调用t1.join()

int _tmain(int argc, _TCHAR* argv[])
{
    std::thread t1(EventHook);
    ... 
    t1.join();
    return 0;
}

【讨论】:

  • 如何避免线程被破坏?我试图将t1 定义为全局变量,现在我得到了这个错误:call of an object of a class type without appropriate operator() or conversion functions to pointer-to-function type
  • 我需要在调用EventHook() 后执行更多代码,这就是我询问如何在辅助线程上运行它的原因。
  • @LeandroBoinha 您的问题中没有任何内容,或者以前的 cmets 说您想在钩子运行时运行其他代码。如果你需要线程,那么在退出前调用join()即可。
  • 我不需要线程,我想要实现的是让 EventHook() 在一个线程上运行并同时在另一个线程中运行我的其余代码。如果我调用 join 那么它将卡在 EventHook() 的消息循环中
  • @LeandroBoinha "我不需要线程" - 你在自相矛盾。 “如果我调用 join 那么它将卡在 EventHook() 的消息循环中” - 不,它不会。 join() 将阻塞调用它的线程,即_tmain() 正在运行的线程。EventHook() 及其消息循环将继续在其自己的线程中畅通无阻地运行。
猜你喜欢
  • 2013-01-07
  • 2021-10-13
  • 2015-09-09
  • 1970-01-01
  • 2018-07-23
  • 1970-01-01
  • 1970-01-01
  • 2019-03-18
  • 2012-10-25
相关资源
最近更新 更多