我有一个对话框类由内存中的对话框模板组成,它运行它自己的嵌套消息循环(除了主应用程序消息循环之外)。
要创建对话框,我使用 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_QUIT
由 OnDestroy
发送处理程序,因此一旦 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
并且不能设置为 nullptr
在OnNcDestroy
处理程序。
最佳答案
好的,经过一段时间的试验后,我对代码进行了以下更改,使一切正常运行! (更多信息见评论)
顺便说一句。我将保留这个问题,希望有更有效的方法,目前 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/