问题描述
我有一个无模式对话框,该对话框以编程方式显示和隐藏在工作线程中。问题在于 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)似乎使对话框出现在顶部出现。
这也意味着我不需要自己的消息循环。