在 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) 不会将窗口置于前面的主要内容,如果未能解决你的问题,请参考以下文章

ATL的特点与基本使用方法

PyQt 无模式对话框不会显示

[WTL/ATL]_[初级]_[关于窗口子类析构时崩溃的原因]

ATL(COM) 开发下 -- VS2010

[WTL/ATL]_[初级]_[关于窗口子类析构时崩溃的原因]

控件不在无模式对话框 MFC 中呈现