【问题标题】:PostMessage() succeeds but my message processing code never receives the messagePostMessage() 成功但我的消息处理代码从未收到消息
【发布时间】:2011-09-23 02:00:42
【问题描述】:

在我的 C++ 应用程序的 GUI 对象中,我在主窗口过程中有以下内容:

case WM_SIZE:
    {
        OutputDebugString(L"WM_SIZE received.\n");
        RECT rect = {0};
        GetWindowRect(hwnd, &rect);
        if (!PostMessage(0, GUI_MSG_SIZECHANGED, w, MAKELONG(rect.bottom - rect.top, rect.right - rect.left))) {
            OutputDebugString(L"PostMessage failed.\n"); // <--- never called
        }
    }

    return 0; // break;

GUI 对象还具有以下 getMessage() 方法:

int GUI::getMessage(MSG & msg) {
    BOOL result = 0;

    while ((result = GetMessage(&msg, 0, 0, 0)) > 0) {
        if (msg.message > (GUI_MSG_BASE-1) && msg.message < (GUI_MSG_LAST+1)) {
            OutputDebugString(L"GUI message received.\n");
            break;
        }
        else {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return result;
}

应用对象调用这个方法的方式如下:

while ((result = _gui.getMessage(msg)) > 0) {
    switch (msg.message) {
        // TODO: Add gui message handlers
        case GUI_MSG_SIZECHANGED:
            OutputDebugString(L"GUI_MSG_SIZECHANGED received.\n");
            _cfg.setWndWidth(HIWORD(msg.lParam));
            _cfg.setWndHeight(LOWORD(msg.lParam));
            if (msg.wParam == SIZE_MAXIMIZED)
                _cfg.setWndShow(SW_MAXIMIZE);
            else if (msg.wParam == SIZE_MINIMIZED)
                _cfg.setWndShow(SW_MINIMIZE);
            else if (msg.wParam == SIZE_RESTORED)
                _cfg.setWndShow(SW_SHOWNORMAL);
            break;
    }
}

应用程序对象对窗口大小感兴趣,因为它将这些信息存储在配置文件中。

当我在 Visual Studio 的调试器中运行它时,调整窗口大小后的输出窗口如下所示:

WM_SIZE received.
GUI message received.
GUI_MSG_SIZECHANGED received.
WM_SIZE received.
WM_SIZE received.
WM_SIZE received.
WM_SIZE received.
...etc...

PostMessage() 函数永远不会失败,但似乎只在第一次处理 WM_SIZE 时发送 GUI_MSG_SIZECHANGED (#defined as WM_APP + 0x000d),也就是在处理 WM_CREATE 之后。

我不知道是什么原因造成的。我尝试使用 SendMessage 和 PostThreadMessage 但结果是一样的。还阅读了 MSDN 的消息处理文档,但找不到我的代码有什么问题。

有人可以帮忙吗?

【问题讨论】:

  • 你为什么不运行一个正常的消息循环。 GUI::getMessage 中的 while 循环到底有什么意义?您似乎有两个嵌套的消息泵!我根本无法理解这一点。如果您运行正常的消息泵,则更有动力尝试回答问题。就目前而言,人们只能猜测发生了什么其他怪事。
  • (msg.message &gt; (GUI_MSG_BASE-1) &amp;&amp; msg.message &lt; (GUI_MSG_LAST+1)) 而不是使用&gt;=&lt;=?为什么?
  • 尝试在进入和离开GUI::getMessage 函数以及围绕_gui.GetMessage 循环之后添加一些调试输出。我认为这是您的程序控制流中的问题,但我无法确定确切的位置。
  • @David 嵌套消息泵是我试图将 GUI 代码和应用程​​序逻辑分开的结果。例如,应用程序对象实际上并不关心 WM_PAINT,因此它由 GUI 对象中的内部泵处理。但是,由于 GUI 也是一种输入机制,因此需要通知 app 对象某些事情,例如当用户选择要打开的文件时,或者在这种情况下,当调整窗口大小时......我猜我的底层问题是OO设计...如果对象B是对象A的成员,B如何通知A某些事件?
  • 嵌套消息泵无法正确解决

标签: c++ winapi user-interface postmessage


【解决方案1】:

破解自定义消息循环是您终有一天会后悔的事情。你早打到了。

不要发布带有 NULL 窗口句柄的消息,它们只有在您可以保证您的程序只泵出您的自定义消息循环时才能工作。您不能做出这样的保证。一旦您启动对话或 Windows 决定自行泵送消息循环,这些消息就会落入位桶。当用户调整窗口大小时,调整大小的逻辑是模态的。 Windows 泵出它自己的消息循环,WM_ENTERSIZEMOVE 宣布它。如果线程能够显示任何窗口,这也是 PostThreadMessage 是 evil 的原因。即使是 MessageBox 也是致命的。 DispatchMessage 无法传递消息。

创建一个充当控制器的隐藏窗口。现在您可以在其窗口过程中检测 GUI_MSG_SIZECHANGED 并且不需要对消息循环进行黑客攻击。顺便说一句,该控制器经常是您应用的主窗口。

【讨论】:

  • 虽然我不太喜欢在我的应用程序对象中隐藏窗口的想法(因为我试图将 GUI 代码保留在我的 GUI 对象中),但这可能是我最终会做的事情如果我想不出更优雅的解决方案。请阅读我对 David 的回复,也许您对我的 OO 设计问题有一些很好的意见。
  • 我可以接受在模态尺寸循环期间线程消息被丢弃在地板上,但是第一个消息是如何通过的? :)
  • @Frédéric - 第一个可能是初始窗口创建时的 WM_SIZE。类似的东西。
  • 这很有意义。谢谢你:)
猜你喜欢
  • 1970-01-01
  • 2018-04-24
  • 2011-05-06
  • 2015-07-17
  • 2012-08-29
  • 1970-01-01
  • 1970-01-01
  • 2013-08-28
  • 1970-01-01
相关资源
最近更新 更多