c++ - 结束自定义对话框的嵌套消息循环

标签 c++ winapi

我有一个对话框类由内存中的对话框模板组成,它运行它自己的嵌套消息循环(除了主应用程序消息循环之外)。

要创建对话框,我使用 CreateDialogIndirectParamW它返回对话窗口的句柄,

由于对话框是完全定制的,所有的消息处理和创建都是手动完成的。

我省略了 Dialog 过程,但基本上它会在对话框关闭后调用下面相关的消息处理程序。

请注意,我们不能使用 EndDialog函数销毁对话框并结束消息循环,因为 CreateDialogIndirectParamW我们必须使用 DestroyWindow明确地。

我正在寻找结束对话循环的有效方法。

我尝试执行 this loop但问题是自 for 以来,这种方法要么消耗过多的 CPU 资源。示例代码中的循环将只是愚蠢地运行,直到有一条消息,或者如果 for然后省略PeekMessage将立即停止循环,这不是我想要的。

相关类声明:

class Dialog :
    public ContainerWindow,
    public MessageWindow,
    public SuperClassWindow
{
    // ...

public:
    /** Process messages for dialog window */
    [[nodiscard]] int RunMessageLoop() override;

protected:
    /** Handler for WM_NCDESTROY */
    std::uint32_t OnNcDestroy(const UINT& uMsg, const WPARAM& wParam, const LPARAM& lParam) override;

    /** Handler for WM_DESTROY */
    inline void OnDestroy() const noexcept override;

    /** Handler for WM_CLOSE */
    inline virtual bool OnClose() noexcept;

    // ...

protected:
        HWND mhWnd;                 /** Window handle of derived component */
    }

简化类定义:

std::uint32_t Dialog::OnNcDestroy(
    [[maybe_unused]] const UINT& uMsg,
    [[maybe_unused]] const WPARAM& wParam,
    [[maybe_unused]] const LPARAM& lParam)
{
    // ...
    delete this; // note we can't set this pointer to nullptr!
    // ...
    return count;
}

void Dialog::OnDestroy() const noexcept
{
    PostQuitMessage(0);
}

bool Dialog::OnClose() noexcept
{
    return DestroyWindow(mhWnd);
}

下面是对话的消息循环: 我需要在循环中添加一些检查代码来检查对话框是否是有效对象,如果对话框对象已被删除,则以某种方式停止循环

一旦OnNcDestroy调用处理程序,IsDialogMessageW波纹管将失败,请参阅评论。

看起来像GetMessageW将在 WM_NCDESTROY 之后继续运行已调度,循环仍在等待WM_QUITOnDestroy 发送处理程序,因此一旦 Dialog 对象被删除,msg 循环将继续运行,这将使 IsDialogMessageW(mhWnd, &msg)失败了。自 mhWnd不存在了。

int Dialog::RunMessageLoop()
{
    EnableWindow(mhWndParent, FALSE);

    MSG msg{ };
    BOOL result = FALSE;

    while ((result = GetMessageW(&msg, nullptr, 0, 0)) != FALSE)
    {
        if (result == -1)
        {
            ShowError(ERR_BOILER); // error checking function.
            break;
        }

        // once OnNcDestroy is called "mhWnd" is invalid memory
        // and this will off course cause access violation!
        if (!IsDialogMessageW(mhWnd, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessageW(&msg);
        }
    }

    EnableWindow(mhWndParent, TRUE);

    return static_cast<int>(msg.wParam);
}

注意我们不能if (this)因为this不是 nullptr并且不能设置为 nullptrOnNcDestroy处理程序。

最佳答案

好的,经过一段时间的试验后,我对代码进行了以下更改,使一切正常运行! (更多信息见评论)

顺便说一句。我将保留这个问题,希望有更有效的方法,目前 IsWindow 为每条消息调用,这可能是一个性能问题。

int Dialog::RunMessageLoop()
{
    EnableWindow(mhWndParent, FALSE);

    MSG msg{ };
    BOOL result = FALSE;

    // first we save the handle to the window
    HWND hwnd = mhWnd;
    if (!IsWindow(hwnd)) // make sure the handle is valid dialog window
        std::abort();

    while ((result = GetMessageW(&msg, nullptr, 0, 0)) != FALSE)
    {
        if (result == -1)
        {
            ShowError(ERR_BOILER);
            break;
        }

        // if object is deleted the handle is no longer valid!
        // because prior to OnNcDestroy handler DestroyWindow has been called
        if (!IsWindow(hwnd))
            goto invalid;

        if (!IsDialogMessageW(mhWnd, &msg))
        {
        invalid: // non dialog message processing
            TranslateMessage(&msg);
            DispatchMessageW(&msg);
        }
    }

    // NOTE that we removed EnableWindow(mHwndParent) here!

    return static_cast<int>(msg.wParam);
}

这本身就可以工作,但一个额外的步骤是在销毁对话框之前启用所有者窗口(主窗口),因此更新处理程序:

bool Dialog::OnClose() noexcept
{
    // otherwise first window in Z order (such as Desktop) will get focus!
    EnableWindow(mhWndParent, TRUE); 

    return DestroyWindow(mhWnd);
}

关于c++ - 结束自定义对话框的嵌套消息循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58692019/

相关文章:

c++ - 编译独立的静态可执行文件

c++ - 从线程更新进度条的最佳方法

c++ - 在遗留 Win32 应用程序中实现 COM 事件接收器

c - INITCOMMONCONTROLSEX 未在此范围内声明

c - 无法将多行文本文件复制到缓冲区

c++ - 如何将 int 字符串与消息框一起使用?

c++ - gdb 无法在 mac os x 中调试 "hello world"

c++ - 通过引用将变量传递给函数是否比使函数返回变量更有效?

c++ - 如何将一个窗口保持在另一个应用程序窗口的前面

c++ - SendNotifyMessage API 是否跨用户 session 工作?