略谈如何从工作线程中弹出对话框
2008-08-27 20:40
239 查看
朱金灿
工作线程,在一些技术文章被称为辅助线程,是相对于主线程而言的。在工作线程中使用界面需要一些技巧。我就曾在工程线程中弹出对话框中遇到过莫名奇妙的错误。下面就我的经验谈谈如何从工作线程中弹出对话框(暂时只讲方法,原理还没彻底弄清楚)。
实际上在工作线程中直接弹出模式对话框中在debug模式下有时出错(这里的有时的意思是必然会出错,但是不是每次都出错),弹出模式对话框的代码如下:
DWORD WINAPI RecvThread(LPVOID lpParam) // 工作线程函数
{
CAIDlgProductName dlg;
if(dlg.DoModal() == IDOK)
{
……
}
}
错误截图:
如果跟踪DoModal函数,我们进入MFC源码找到出错的地方:
#ifdef _DEBUG
void CWnd::AssertValid() const
{
if (m_hWnd == NULL)
return; // null (unattached) windows are valid
// check for special wnd??? values
ASSERT(HWND_TOP == NULL); // same as desktop
if (m_hWnd == HWND_BOTTOM)
ASSERT(this == &CWnd::wndBottom);
else if (m_hWnd == HWND_TOPMOST)
ASSERT(this == &CWnd::wndTopMost);
else if (m_hWnd == HWND_NOTOPMOST)
ASSERT(this == &CWnd::wndNoTopMost);
else
{
// should be a normal window
ASSERT(::IsWindow(m_hWnd));
// should also be in the permanent or temporary handle map
CHandleMap* pMap = afxMapHWND();
ASSERT(pMap != NULL);
CObject* p;
// 在下面一句出错
ASSERT((p = pMap->LookupPermanent(m_hWnd)) != NULL ||
(p = pMap->LookupTemporary(m_hWnd)) != NULL);
ASSERT((CWnd*)p == this); // must be us
// Note: if either of the above asserts fire and you are
// writing a multithreaded application, it is likely that
// you have passed a C++ object from one thread to another
// and have used that object in a way that was not intended.
// (only simple inline wrapper functions should be used)
//
// In general, CWnd objects should be passed by HWND from
// one thread to another. The receiving thread can wrap
// the HWND with a CWnd object by using CWnd::FromHandle.
//
// It is dangerous to pass C++ objects from one thread to
// another, unless the objects are designed to be used in
// such a manner.
}
}
实际上当时给我启发的是上面那段Note。我用我浅薄的英文功底翻译一下大意就是:就是上面的asserts发生了同时你正在写的是一个多线程程序,那么asserts发生的原因很可能是你将一个C++对象从一个线程传递给另一个线程同时你无意中使用了那个C++对象(only simple inline wrapper functions should be used(抱歉,这一句不会翻译)),实际上线程之间传递CWnd对象应该传递句柄(HWND)。接收线程应该通过CWnd::FromHandle函数通过传递过来的句柄获取CWnd对象(这里准确的来说应该是CWnd对象的指针)。
线程之间传递C++对象是危险的,除非那个对象被设计为以那种方式使用。
由上面我想到一种在工作线程中弹出的对话框的办法:
1. 转递视图类句柄给线程函数:
HWND HView;
…… // 获取视图类句柄
CreateThread(NULL,0,RecvThread, HView
,0,&dwThreadId);
2. 在线程函数中通过句柄获取视图类指针,获取数据给视图类发送自定义消息:
DWORD WINAPI RecvThread(LPVOID lpParam)
{
HWND HView = (HWND)lpParam;
CWnd* pMyView = CWnd::FromHandle(HView);
……
pMyView ->SendMessage(WM_TASKDLG_MESSAGE,(WPARAM)(&str));
…….
}
3. 在视图类自定义一个消息函数OnTaskDlgMessage专门处理WM_TASKDLG_MESSAGE消息用于创建对话框:
LRESULT CInteAView::OnTaskDlgMessage(WPARAM wParam, LPARAM lParam)
{
CAIDlgProductName dlg;
if(dlg.DoModal() == IDOK)
{
……
}
return 0;
}
当然上面将视图类换为框架类也是可以的。上面就我的经验谈了一种从工作线程中弹出对话框的办法,不当之处还请大家指点。
参考文献:
1.关于多线程中传递MFC窗口类指针时ASSERT_VALID出错的另类解决
2.MFC中创建多线程 MFC对象指针不能在线程间传输
工作线程,在一些技术文章被称为辅助线程,是相对于主线程而言的。在工作线程中使用界面需要一些技巧。我就曾在工程线程中弹出对话框中遇到过莫名奇妙的错误。下面就我的经验谈谈如何从工作线程中弹出对话框(暂时只讲方法,原理还没彻底弄清楚)。
实际上在工作线程中直接弹出模式对话框中在debug模式下有时出错(这里的有时的意思是必然会出错,但是不是每次都出错),弹出模式对话框的代码如下:
DWORD WINAPI RecvThread(LPVOID lpParam) // 工作线程函数
{
CAIDlgProductName dlg;
if(dlg.DoModal() == IDOK)
{
……
}
}
错误截图:
如果跟踪DoModal函数,我们进入MFC源码找到出错的地方:
#ifdef _DEBUG
void CWnd::AssertValid() const
{
if (m_hWnd == NULL)
return; // null (unattached) windows are valid
// check for special wnd??? values
ASSERT(HWND_TOP == NULL); // same as desktop
if (m_hWnd == HWND_BOTTOM)
ASSERT(this == &CWnd::wndBottom);
else if (m_hWnd == HWND_TOPMOST)
ASSERT(this == &CWnd::wndTopMost);
else if (m_hWnd == HWND_NOTOPMOST)
ASSERT(this == &CWnd::wndNoTopMost);
else
{
// should be a normal window
ASSERT(::IsWindow(m_hWnd));
// should also be in the permanent or temporary handle map
CHandleMap* pMap = afxMapHWND();
ASSERT(pMap != NULL);
CObject* p;
// 在下面一句出错
ASSERT((p = pMap->LookupPermanent(m_hWnd)) != NULL ||
(p = pMap->LookupTemporary(m_hWnd)) != NULL);
ASSERT((CWnd*)p == this); // must be us
// Note: if either of the above asserts fire and you are
// writing a multithreaded application, it is likely that
// you have passed a C++ object from one thread to another
// and have used that object in a way that was not intended.
// (only simple inline wrapper functions should be used)
//
// In general, CWnd objects should be passed by HWND from
// one thread to another. The receiving thread can wrap
// the HWND with a CWnd object by using CWnd::FromHandle.
//
// It is dangerous to pass C++ objects from one thread to
// another, unless the objects are designed to be used in
// such a manner.
}
}
实际上当时给我启发的是上面那段Note。我用我浅薄的英文功底翻译一下大意就是:就是上面的asserts发生了同时你正在写的是一个多线程程序,那么asserts发生的原因很可能是你将一个C++对象从一个线程传递给另一个线程同时你无意中使用了那个C++对象(only simple inline wrapper functions should be used(抱歉,这一句不会翻译)),实际上线程之间传递CWnd对象应该传递句柄(HWND)。接收线程应该通过CWnd::FromHandle函数通过传递过来的句柄获取CWnd对象(这里准确的来说应该是CWnd对象的指针)。
线程之间传递C++对象是危险的,除非那个对象被设计为以那种方式使用。
由上面我想到一种在工作线程中弹出的对话框的办法:
1. 转递视图类句柄给线程函数:
HWND HView;
…… // 获取视图类句柄
CreateThread(NULL,0,RecvThread, HView
,0,&dwThreadId);
2. 在线程函数中通过句柄获取视图类指针,获取数据给视图类发送自定义消息:
DWORD WINAPI RecvThread(LPVOID lpParam)
{
HWND HView = (HWND)lpParam;
CWnd* pMyView = CWnd::FromHandle(HView);
……
pMyView ->SendMessage(WM_TASKDLG_MESSAGE,(WPARAM)(&str));
…….
}
3. 在视图类自定义一个消息函数OnTaskDlgMessage专门处理WM_TASKDLG_MESSAGE消息用于创建对话框:
LRESULT CInteAView::OnTaskDlgMessage(WPARAM wParam, LPARAM lParam)
{
CAIDlgProductName dlg;
if(dlg.DoModal() == IDOK)
{
……
}
return 0;
}
当然上面将视图类换为框架类也是可以的。上面就我的经验谈了一种从工作线程中弹出对话框的办法,不当之处还请大家指点。
参考文献:
1.关于多线程中传递MFC窗口类指针时ASSERT_VALID出错的另类解决
2.MFC中创建多线程 MFC对象指针不能在线程间传输
相关文章推荐
- 略谈如何从工作线程中弹出对话框
- 略谈如何从工作线程中弹出对话框
- 略谈如何从工作线程中弹出对话框
- 工作线程中弹出对话框
- 略谈如何在对话框创建视图类画图
- Flex Builder 3 如何弹出“打开文件对话框”,限制选择文件类型
- 略谈如何在对话框创建视图类画图
- 略谈如何在对话框创建视图类画图
- 驰骋工作流程引擎,ccflow,如何把子线程的数据汇总到合流节点表单中去?
- WPF:如何在工作线程中更新窗体的UI元素(Dispatcher机制)
- Android如何在广播接收器中启动活动和弹出对话框
- MVC4下如何实现模态弹出对话框效果--对话框里可以实现翻页效果(1)
- 工作线程调用sendmessage()向主对话框发送自定义消息遇到的问题以及解决
- 在线程中如何获得主对话框中的指针?
- iOS 如何创建一个线程,要求可以一直工作,不会执行一次就结束
- 如何在DetailsView中单击“删除”按钮时弹出对话框
- Android 如何监听返回键,弹出一个退出对话框
- 如何在程序里使用代码关闭由MessageBox()弹出的对话框?
- 如何解决IIS停止后重启,IIS重装后出现弹出对话框“拒绝您访问此计算机”问题
- 在Asp.net中如何实现弹出提示对话框