在 ATL 无模式对话框上调用 ShowWindow(SW_SHOW) 不会将 window 置于最前面

Calling ShowWindow(SW_SHOW) on ATL modeless dialog not bringing window to front

我有一个无模式对话框,我通过工作线程以编程方式显示和隐藏它。问题是 CWindow::ShowWindow(SW_SHOW) 函数有时会将对话框置于最前面,有时则不会。

任务:Show/Hide ATL COM 对象 (dll) 的简单日志记录和控制 window 运行 在单独的线程中。 COM 对象不是控件:它用于包装用于 Excel VBA.

的处理代码

我创建了一个从 CAxDialogImpl 模板派生的 CMyDialog class。然后我将 CWorkerThread 模板用于工作线程。这是执行块(即 运行 工作线程中的函数)。

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;
}

我的 Log 对象有两个函数,StartLog() 和 StopLog(),它们由执行数据处理主要工作的 COM 对象调用。

void CLogProcessor::StartLog()
{
    if (!m_bRunning)
    {
        ::SetEvent(m_hStartEvent);
    }
}

void CLogProcessor::StopLog()
{
    if (m_bRunning)
    {
        ::SetEvent(m_hStopEvent);
    }
}

这很好用。我 运行 VBA 在 Excel 电子表格中编写代码,其中包含调用(通过 COM 对象)StartLog() 和 StopLog() 的按钮。日志对话框第一次出现时一切正常:它被激活并出现在最前面。触发 StopLog() 关闭对话框并消失。但是在我调用 StartLog() 的 时,对话框 window 出现了,但它不在前面,而是在其他 windows 后面。我已经尝试在 ShowWindow() 之后直接调用 CWindow::BringWindowToTop() and/or SetFocus() 但这没有区别.

另一个有趣的功能是我的对话框有一个系统菜单,因此 'X' 可以在右上角关闭它。在对话框的消息映射中,我正在处理 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() 代码到我的对话框:它关闭了 window,但它没有再次出现在顶部。另一个区别是,在对话框上使用 Close X 意味着对话框在关闭时具有焦点。从 Excel 按钮关闭它意味着 Excel window 有焦点。

我知道这是一个很长的问题,如果你一直坚持到最后,非常感谢!我一直热衷于任何解决方案/见解。

更新:我没有按照所述解决问题,只是 re-worked 在工作线程中使用模态对话框的代码。当我完成对话框时,我从主线程 PostMessage(WM_CLOSE) :对话框结束并且工作线程完成(设置了一个事件来说明它已经完成,这样线程就不会在对话框已清理)。

对话框轮询要显示的字符串堆栈以响应使用 PostMessage() 从主线程发布的 WM_USER+xxx 消息;

我仍然有同样的问题,即我第二次 运行 工作线程时对话框没有出现在顶部,但这次在 OnInitDialog() 处理程序中添加了一个 ShowWindow(SW_SHOW)似乎在对话框出现时将其置于顶部。

这也意味着我不需要自己的消息循环。