读书人

小弟我想了几天的有关问题,请各位兄弟

发布时间: 2012-01-24 23:11:54 作者: rapoo

我想了几天的问题,请各位兄弟来看看,急我所难!!线程传参
这个例子是 VC++ 高级编程技术与实例 宋哓宇 王永会 编著.中国水利水电出版社 2006年1月第一版 价格37. 第五章多线程的一个例子
1:
建立一个基于对话框的程序Test
2:
在对话框上放置一个按钮IDC_BT_START.和一个进度条控件IDC_PROGRESS1
并为IDC_PROGRESS1关联一个变量m_progress;
3:
在TestDlg.h文件中,定义一个结构体
struct threadInfo
{
CProgressCtrl * p_Progress;
CDialog * p_Dlg;
int second; //进度条暂停时

}
4:
在TestDlg.cpp文件中
#define USER_PROC_FINISHED WM_USER+1

threadInfo Info;
UINT ThreadProc(LPVOID pParam)
{
threadInfo * p=(threadInfo *)pParam;
for(int i=0;i <=100;i++)
{
p-> p_Progress-> SetPos(i);
::Sleep(p-> second*10);


}
::PostMessage(p-> p_Dlg-> GetSafeHwnd(),USER_PROC_FINISHED,0,0);
return 0;
}
5:
在按钮IDC_BT_START的单击响应中:
void CTestDlg::OnBtStart()
{

Info.p_Progress=&m_progress;
Info.p_Dlg=this;
Info.second=1;
AfxBeginThread(ThreadProc,&Info);
}
6:
在TestDlg.h中声明消息函数

.........
//}}AFX_MSG
afx_msg void OnFinish();
7.
在TestDlg.cpp文件中
BEGIN_MESSAGE_MAP(CTestDlg, CDialog)
.......
ON_BN_CLICKED(IDC_BT_START, OnBtStart)
//}}AFX_MSG_MAP
ON_MESSAGE(USER_PROC_FINISHED,OnFinish)
END_MESSAGE_MAP()

void CTestDlg::OnFinish()
{
SetDlgItemText(IDC_BT_START, "线程结束请重新启动 ");
}


在候捷译的 <Win32 多线程程序设计> 书中p294中说:
1:
MFC有一个重大限制,会影响你所做的几乎每一件事情,MFC各对象和Win32 handles之间的映射关系记录在线程局部存贮中(TLS),因此你没有办法把一个MFC对象从某个线程手上交到另一个线程手上,你也不能在线程之间传递MFC对象指针.这里的指针包含(担不限于)CWnd,CDC,CPen,CBrush,CFont,Cbitmap,CPalette........
2:这个限制的意思是说,你不能够放一个指针(指向一个CWnd)到一个结构中,而该结构被一个Worker线程使用,你也不能够把一个指向CDialog或CView的指针交给另一个线程.............在线程之间共享对象,这里倒有一个不大方便的替代方案,不要放置MFC对象,改放对象的handle,你可以利用GetSateHwnd()获得派生自CWnd对象的handle,CDialog.

上各程序中
void CTestDlg::OnBtStart()
{

Info.p_Progress=&m_progress; //传递进度条的地址,不对吧?? 进度条派生自CWnd
Info.p_Dlg=this; //传递对话框的指针,不对吧?? 进度条派生自CWnd
Info.second=1;
AfxBeginThread(ThreadProc,&Info);
}


threadInfo Info;
UINT ThreadProc(LPVOID pParam)
{
threadInfo * p=(threadInfo *)pParam;
for(int i=0;i <=100;i++)
{
p-> p_Progress-> SetPos(i);//利用所传进度条的地址,调用进度条的成员函数,不对吧??.
::Sleep(p-> second*10);


}
::PostMessage(p-> p_Dlg-> GetSafeHwnd(),USER_PROC_FINISHED,0,0);
//(p-> p_Dlg-> GetSafeHwnd() 利用所传对话框的this指针,调用成员函数GetSafeHwnd(),不对吧??.
return 0;
}



[解决办法]
国内的书有很不认真的. 侯捷是对的, MSDN上也是这么说的.

通过传递HANDLE而不是对象的指针, 你可以在你的worker thread中用CWnd::FromHandle或CWnd::Attach来将HANDLE绑定到在你这个worker thread中的一个C++对象上.

还可以通过给主线程PostMessage来完成一定的工作
[解决办法]


你确定m_progress确实拥有一个有效的m_hWnd吗?

或者是不是没有testdlg.Detach()的原因?
[解决办法]

testdlg.Attach((HWND)pParam);
一个窗口应该只能被一个对象Attach吧.我跟踪了下,点击 "开始 "按钮之后的断言,就出现在这里.

这样做,可以使testdlg.Attach((HWND)pParam);运行成功:

AfxBeginThread(ThreadProc,this-> Detach());
在现成结束之后,再 this-> Attach...

但是同样没有任何意义,因为在testdlg Attach成功之后,监视可以发现,testdlg的成员变量m_progress1为NULL,这样运行到
testdlg.m_progress.SetPos(i);这句话时,是肯定会出现内存错误的.

其实说的是往线程传参数是,最好是handle,但是前面传指针,还不是用的好好的?干麻非要传handle呢?handle作为参数传进去的时候,好象做PostMessage(..)的第一个参数时,是没有什么问题的,至于为什么不能用他来操作窗口,那就要等牛人出现给个解释了!

[解决办法]
1.传对象指针是可以的
2.但不能调用与句柄映射有关的函数。比如CWnd::GetDlgItem().只要不导致下面这个函数和其它句柄映射有关的函数,就可以。

CWnd* PASCAL CWnd::FromHandle(HWND hWnd)
{
CHandleMap* pMap = afxMapHWND(TRUE); //create map if not exist
ASSERT(pMap != NULL);
CWnd* pWnd = (CWnd*)pMap-> FromHandle(hWnd);

#ifndef _AFX_NO_OCC_SUPPORT
pWnd-> AttachControlSite(pMap);
#endif

ASSERT(pWnd == NULL || pWnd-> m_hWnd == hWnd);
return pWnd;
}

读书人网 >VC/MFC

热点推荐