在ATL无模式对话框中调用ShowWindowSW_SHOW不会将窗口置于最前面

问题描述

我有一个无模式对话框,该对话框以编程方式显示和隐藏在工作线程中。问题在于 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,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,(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);
    }
}

这很好。我在Excel电子表格中使用调用按钮(通过COM对象)StartLog()和StopLog()来运行VBA代码。第一次出现“日志对话框”时,一切都很好:它已激活并显示在最前面。触发StopLog()将关闭对话框,然后对话框消失。但是第二次,我调用StartLog()时,对话框窗口出现,但它不在前面,而在其他窗口后面。我已经在ShowWindow()之后尝试了 CWindow :: BringWindowToTop()和/或 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()代码:它关闭了窗口,但再次没有出现在顶部。另一个区别是,在对话框上使用“关闭X”意味着对话框关闭时具有焦点。通过Excel按钮关闭它意味着Excel窗口具有焦点。

我知道这是一个很长的问题,非常感谢您一直坚持到底!我一直渴望任何解决方案/见解。

解决方法

更新:我没有按照所述解决问题,只是重新编写了代码以在工作线程中使用模式对话框。在完成对话框后,我从主线程中发出PostMessage(WM_CLOSE):对话框结束,工作线程完成(已设置一个事件来说明已完成,因此在清除Dialog之前不会破坏线程)。

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

我仍然遇到相同的问题,即第二次运行辅助线程时,对话框没有出现在顶部,但是这次在OnInitDialog()处理程序中添加ShowWindow(SW_SHOW)似乎使对话框出现在顶部出现。

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