在 ATL 无模式对话框上调用 ShowWindow(SW_SHOW) 不会将窗口置于前面
Posted
技术标签:
【中文标题】在 ATL 无模式对话框上调用 ShowWindow(SW_SHOW) 不会将窗口置于前面【英文标题】:Calling ShowWindow(SW_SHOW) on ATL modeless dialog not bringing window to front 【发布时间】:2020-12-26 15:38:41 【问题描述】:我有一个无模式对话框,我正在以编程方式从工作线程中显示和隐藏它。问题在于 CWindow::ShowWindow(SW_SHOW) 函数有时会将对话框置于最前面,有时则不会。
任务:显示/隐藏在单独线程中运行的 ATL COM 对象 (dll) 的简单日志记录和控制窗口。 COM 对象不是控件:它用于包装处理代码以在 Excel VBA 中使用。
我创建了一个从 CAxDialogImpl 模板派生的 CMyDialog 类。然后我将 CWorkerThread 模板用于工作线程。这是 Execute 块(即在工作线程中运行的函数)。
HRESULT CLogProcessor::Execute(DWORD_PTR dwParam, HANDLE hObject)
m_bRunning = true; //Winging it as this isn't thread-safe, but I don't think that's the issue.
if (m_pDlg == 0)
m_pDlg = new CMyDialog(); //My dialog class
m_pDlg->SetStopEvent(m_hStopEvent); //Save the handle of the stop event in the Dialog class
m_pDlg->Create(NULL); //Create with no parent
m_pDlg->ShowWindow(SW_SHOW); //Show the window
//Loop here until told to stop by the Stop Event being signalled in the calling thread
while (WaitForSingleObject(m_hStopEvent, 0) == WAIT_TIMEOUT) //Polling Stop Event
//Have to keep the message loop going!
MSG uMsg;
while (PeekMessage(&uMsg, m_pDlg->m_hWnd, 0, 0, PM_REMOVE))
TranslateMessage(&uMsg);
DispatchMessage(&uMsg);
//The thread uses a protected "stack" of string messages to write to my logging window
string strMsg;
while (m_MsgStack.PopFront(strMsg))
std::wstring ws = std::wstring(strMsg.begin(), strMsg.end());
CWindow eb(m_pDlg->GetDlgItem(IDC_EDIT1));
eb.SendMessage(EM_REPLACESEL, 0, (LPARAM)ws.c_str());
//Stop event has been signalled so clear up the dialog
m_pDlg->DestroyWindow();
delete m_pDlg;
m_pDlg = 0;
m_bRunning = false;
return S_OK;
My Log 对象有两个函数,StartLog() 和 StopLog(),由 COM 对象调用,COM 对象做数据处理的主要工作。
void CLogProcessor::StartLog()
if (!m_bRunning)
::SetEvent(m_hStartEvent);
void CLogProcessor::StopLog()
if (m_bRunning)
::SetEvent(m_hStopEvent);
这很好用。我在 Excel 电子表格中使用调用(通过 COM 对象)StartLog() 和 StopLog() 的按钮运行 VBA 代码。日志对话框第一次出现时一切都很好:它被激活并出现在最前面。触发 StopLog() 会关闭对话框并消失。但是我调用 StartLog() 第二次 时,对话框窗口出现,但它不在前面,而是在其他窗口后面。我在 ShowWindow() 之后直接尝试了 CWindow::BringWindowToTop() 和/或 SetFocus() 调用,但没有区别。
另一个有趣的功能是我的对话框有一个系统菜单,因此右上角有一个“X”来关闭它。在 Dialog 的消息映射中,我正在处理 WM_CLOSE 消息:
BEGIN_MSG_MAP(CMyDialog)
... Other entries
MESSAGE_HANDLER(WM_CLOSE, OnClose)
...
CHAIN_MSG_MAP(CAxDialogImpl<CMyDialog>)
END_MSG_MAP()
使用 OnClose() 设置停止事件句柄(之前传递给 Dialog 对象)。即与 StopLog() 函数几乎相同。
LRESULT CMyDialog::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
::SetEvent(m_hStopEvent);
return 0;
如果我用鼠标单击关闭日志对话框,它会像以前一样消失。但是下次我调用 StartLog() 时,对话框会执行它应该做的事情并来到前面,这就是我想要的操作。所以我用 SendMessage(WM_CLOSE) 替换了 StopLog() 代码到我的对话框:它关闭了窗口,但它再次没有重新出现在顶部。另一个区别是在对话框上使用 Close X 意味着对话框在关闭时具有焦点。从 Excel 按钮关闭它意味着 Excel 窗口具有焦点。
我知道这是一个相当长的问题,如果你坚持到最后,非常感谢!我一直热衷于任何解决方案/见解。
【问题讨论】:
【参考方案1】:更新:我没有按照说明解决问题,只是重新编写代码以在工作线程中使用模式对话框。当我完成对话框时,我从主线程发布消息(WM_CLOSE):对话框结束并且工作线程完成(设置了一个事件来表示它已经完成,以便在清理对话框之前线程不会被破坏)。
对话框轮询要显示的字符串堆栈以响应 WM_USER+xxx 消息,使用 PostMessage() 从主线程发布;
我仍然有同样的问题,即第二次运行工作线程时对话框没有出现在顶部,但是这次在 OnInitDialog() 处理程序中添加 ShowWindow(SW_SHOW) 似乎将对话框置于顶部出现。
这也意味着我不需要自己的消息循环。
【讨论】:
以上是关于在 ATL 无模式对话框上调用 ShowWindow(SW_SHOW) 不会将窗口置于前面的主要内容,如果未能解决你的问题,请参考以下文章
[WTL/ATL]_[初级]_[关于窗口子类析构时崩溃的原因]