【问题标题】:Order of execution of message callback function in Visual C++Visual C++中消息回调函数的执行顺序
【发布时间】:2015-10-09 03:27:52
【问题描述】:

我正在开发一个 Windows Visual C++ 应用程序,它将监视各种事件的消息泵。这是我的主要 cpp 文件的骨架:

LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow) {
    HWND hwnd;
    WNDCLASSEX wincl;

    // register WindowProcedure() as message callback function
    wincl.lpfnWndProc = WindowProcedure;
    // assign other properties...

    if (!RegisterClassEx (&wincl))
        return 0;

    // create main window
    hwnd = CreateWindowEx ( ... );

    // infinite message loop
    while (GetMessage (&messages, NULL, 0, 0)) {
        TranslateMessage(&messages);
        DispatchMessage(&messages);
    }

    return 0;
}

这里是回调函数的骨架:

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch(message) {
        case WM_CLIPBOARDUPDATE:
            // handle the update here
    }
}

我的简单问题是回调函数是否保证以顺序开始和结束,或者它们是否可能以并行重叠的方式执行?换句话说,是否有可能对回调函数的 2 个调用可以并行执行,从而可能导致竞争条件?或者 Windows 是否保证每条消息都以串行方式处理,一次一条?欢迎您提供任何文档或参考资料。

【问题讨论】:

  • 与您的问题并不真正相关,但您应该从 WinMain 返回 messages.wParam,而不是 0。当您在处理例如 WM_DESTROY 时调用 PostQuitMessage(0) 时,“0”(参数PostQuitMessage) 将是 GetMessage 循环退出后 messages.wParam 的值。

标签: c++ winapi visual-c++ message-queue


【解决方案1】:

窗口消息存储在队列中。每次调用 GetMessage 时,它​​都会从队列中删除第一条消息。当您调用 DispatchMessage 时会调用您的窗口过程。

所以是的,消息是按顺序处理的。但是,如果在您的窗口过程中调用 SendMessage,则可能会出现一些重叠,因为该函数会绕过消息队列并直接调用您的窗口过程(而不是 PostMessage,它只是将消息放入队列中)。

但这并不意味着窗口过程将并行执行(就像在多个线程中一样)。 DispatchMessage 和 SendMessage 都不会创建不同的线程来运行窗口过程。

【讨论】:

  • 感谢您的快速回复。所以换句话说,假设我没有从我的回调中调用SendMessage(),那么对回调函数的给定调用保证在新调用(可能来自另一个 Windows 消息)开始之前完全完成?
  • 是的,SendMessage 或任何其他导致调用窗口过程的东西(例如 UpdateWindow)。但是从窗口过程调用 SendMessage 或类似的东西并不一定是坏事。例如,有时通过调用 UpdateWindow 来在​​每次调整窗口大小时重新绘制某些内容很有用。
  • 我问这个的原因是我正在使用多线程,我需要知道当前正在处理哪条消息。值得庆幸的是,C++ 似乎按顺序处理消息。
  • 如果您从没有创建窗口句柄的线程调用 SendMessage,程序将切换到创建窗口的线程并在窗口过程回调函数中处理消息。调用线程一直等到调用完成。如果您从线程调用 PostMessage,它会在窗口的消息队列中放置一条消息(它不会等待)。无论如何,窗口过程都是在创建窗口的线程上执行的。
  • @TimBiegeleisen:回答你的第一个问题:正确,但不完整。您可能不会直接调用SendMessage,但您调用的 Windows API 会。也值得一读:When can a thread receive window messages? 这篇文章说明了另一种可重入的途径。 @jensa:SendMessage 不会切换到另一个线程。调用线程只是简单地进入睡眠状态,操作系统的线程调度从那里开始。调用线程仍然可以调度传入消息发送消息。
【解决方案2】:

窗口消息有时会存储在消息队列中,有时则不会。未存储在队列中的窗口消息的示例是 WM_ACTIVATE、WM_SETFOCUS 和 WM_SETCURSOR。其他消息,如 WM_PAINT,放在消息队列中。

从不并行调用窗口的窗口过程(参见上一个答案)。

消息确实是按顺序处理的,GetMessage 从队列中拉出第一条消息。然而,需要注意的一件重要事情是,消息可能会在不被处理的情况下从队列中“消失”。例如,windows 在内部为每个窗口维护一个结构 (PAINTSTRUCT),它在其中跟踪窗口的无效区域。一旦窗口客户区的任何部分失效,窗口就会在消息队列中放置一条 WM_PAINT 消息。但是,如果验证了无效区域(例如调用 ValidateRect),Windows 实际上会从队列中删除消息。此外,如果将另一个区域添加到无效区域,Windows 不会在队列中放置另一个 WM_PAINT 消息,它会使用更新的区域更新已放置的 WM_PAINT 消息。

【讨论】:

    猜你喜欢
    • 2013-11-24
    • 1970-01-01
    • 2016-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 2010-12-25
    • 2011-01-31
    相关资源
    最近更新 更多