MFC在子线程中关闭主线程对话框

方法一,在子线程中通过获得主线程中对应对话框的指针,然后向对话框发送WM_CLOSE消息。代码如下

在主线程对话框添加两个按钮,一个用来显示非模态对话框,另一个按钮用来启动子线程,在子线程中调用主线程全局对话框指针,通过该指针,再调用主线程中的函数关闭对话框。按钮一的代码如下

void CTestMutiThreadDestroyWindowDlg::OnButton1() 
{
	// TODO: Add your control notification handler code here
	CDlg2* pDlg = new CDlg2;
	pDlg->Create(IDD_DIALOG1,this);
	pDlg->ShowWindow(SW_NORMAL);
}
按钮二的代码如下

void CTestMutiThreadDestroyWindowDlg::OnButton2() 
{
	AfxBeginThread(Proc1,this);
}

线程函数如下

UINT Proc1(PVOID pRam)
{

	pMainDlg->CloseThreadWindow();  //pMainDlg是主线程的全局对话框指针
	return 0;
}
ClostThreadWindow函数代码如下

void CTestMutiThreadDestroyWindowDlg::CloseThreadWindow()
{
	CDlg2 * pDlg = (CDlg2*)FindWindow(NULL,"ceshi");

	pDlg->PostMessage(WM_CLOSE);
}
在CDlg2中重写WM_CLOSE消息。

void CDlg2::OnClose() 
{
	// TODO: Add your message handler code here and/or call default
	DestroyWindow();
//	CDialog::OnClose();
}
通过测试发现,程序可以正常利用子线程来关闭对话框。

进一步测试发现,如果开启两个对话框,那么如果点击三次子线程来关闭对话框的话,程序就会报错。错误为

“Unhandled exception in TestMutiThreadDestroyWindow.exe(MFC42D.DLL):0xC000005: Access Violation”,点击确定,程序就定位在

{ ASSERT(::IsWindow(m_hWnd)); return ::PostMessage(m_hWnd, message, wParam, lParam); }”

这是因为窗口已经被销毁,所以调用函数自然就错误了。如果CloseThreadWindow函数变为

void CTestMutiThreadDestroyWindowDlg::CloseThreadWindow()
{
	CDlg2 * pDlg = (CDlg2*)FindWindow(NULL,"ceshi");
 	if (pDlg != NULL)
 	{
		pDlg->PostMessage(WM_CLOSE);
	}
	
}

就是没有问题了。程序可以正常运行了。但是有一个问题是,假如程序运行到if(pDlg != NULL)这句,刚好有其它线程的其它的函数关闭了该对话框,那么恐怕pDlg->PostMessage(WM_CLOSE)依然会出现错误。程序依然会报上述错误,看来if(pDlg != NULL)也不是特别保险。那该怎么办呢?想到的方法是对该段代码进行线程同步,加上临界区代码的方法。

方法一固然可行,但是未免有些绕来绕去,不小心就绕晕了,还有一种方法,是利用在子线程中获取主线程创建的对话框的句柄,用该句柄来发送WM_CLOSE消息。代码如下:

子线程代码如下:

UINT Proc1(PVOID pRam)
{

	HWND hWnd = FindWindow(NULL,"ceshi");
	::PostMessage(hWnd,WM_CLOSE,0,0);
	return 0;
}
可以看到上述代码非常简洁。测试上述代码有一个重要的发现,那就是,即使是创建两个对话框,发送三个关闭该对话框的消息,程序依然不会报错。这是应为,如果是空对话框指针调用成员函数程序自然会报错,如果是向空的句柄发送消息,则对程序没有影响。而且,也省去了线程同步的麻烦,岂不是一举两得!

网上关于上述话题有一段重要的话(点击打开链接

简单的说,不建议跨线程访问MFC窗口对象。

MFC句柄封装类只在创建句柄的线程中有效,在其它线程中访问会出现无法预料的结果。适当的访问方式是直接访问句柄。

点击打开链接


你传送CWnd指针是极其错误的做法。MFC不是线程安全的库,它的内部有一个map类,负责将hwnd映射到对象,这个map类是保存在tls中的,线程切换后,不能保证传进来的CWnd指针指向的对象在本线程的tls中依然存在,跨线程必须传递句柄。







展开阅读全文

没有更多推荐了,返回首页