【发布时间】: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 窗口具有焦点。
我知道这是一个相当长的问题,如果你坚持到最后,非常感谢!我一直热衷于任何解决方案/见解。
【问题讨论】:
标签: windows multithreading visual-c++ atl