打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
请教个小问题, 跨线程传窗口对象指针,程序崩溃
UINT Ctest8Dlg:: ThreadProc(LPVOID p){/*Ctest8Dlg* pthis=(Ctest8Dlg*)p;pthis->m_str=_T("hello");pthis->UpdateData(FALSE);*/Ctest8Dlg obj;HWND hWnd=(HWND)p;obj.Attach(hWnd);obj.m_str=_T("xx");obj.UpdateData(FALSE);obj.Detach();return 0;}代码出现的意义在于验证 网上流传的跨线程传递窗口对象指针的法子是否正确,所以回答:采用发送消息给ui线程的人 勿扰。谢谢。



问题2: 

故意多次去释放dll里的内存,程序死活不崩溃。 

dll里: 

char* _stdcall Test() 

return  new char[50]; 

}; 

exe里: 

for(int i=0; i<50;i++) 

char* p=Test(); 
delete []p; 


这么多次循环, 程序也不崩溃 




回复讨论(解决方案)

用指针: 
Ctest8Dlg *pDlg = FromHandle(hWnd);
用指针: 
Ctest8Dlg *pDlg = FromHandle(hWnd); 

pthis->m_str=CString(_T("hello")); 
按照你的修改,这一句崩溃了 

原因不详细。 







另外: 你提供的法子,似乎是什么临时的,网上有,但是我没有看懂,临时是否说明: 

有隐患呢? 

2个问题,谢谢解答。 

问题1: 
因为AfxBeginThread内部已经Attach了一次主界面 
		CWnd threadWnd;....		// thread inherits app's main window if not already set		CWinApp* pApp = AfxGetApp();...		threadWnd.Attach(pApp->m_pMainWnd->m_hWnd);		pThread->m_pMainWnd = &threadWnd;

所以再次Attach是会断言在 
ASSERT(FromHandlePermanent(hWndNew) == NULL);		// must not already be in permanent map

同一线程不能多次Attach同一窗口? 
忽略弹出窗口,可继续执行。 

如果用CWnd::FromHandle,获得的将是上面的(&threadWnd)指针 
由于是CWnd实体对象,强转为Ctest8Dlg对象后,因为m_str不是CWnd成员,pthis->m_str可能会破坏内存 或出现不可预料的结果 

解决方法之一是将AfxBeginThread换成_beginthreadex(NULL, 0, ThreadProc, m_hWnd, 0, NULL); 
另外记得在ThreadProc加上WINAPI(__stdcall) 
static UINT WINAPI ThreadProc(LPVOID p);

使用Attach的方法 
如果用CWnd::FromHandle,还是会出错,因为此时获得是临时的CWnd*对象指针,强转为Ctest8Dlg*后,同样会出错。 


问题2 
这个起因是分配和释放的CRT Heap不相同造成 
CRT源码里有一个全局句柄HANDLE _crtheap 

如果exe和dll都使用动态DLL编译,那么这个_crtheap只会存在一个,在msvcrXX.dll里 
另外3中情况,静态编译会将_crtheap编译进去,就会出错 
exe动态 和 dll静态编译:msvcrXX.dll和dll各1份_crtheap 
exe静态 和 dll动态编译:msvcrXX.dll和exe各1份_crtheap 
exe静态 和 dll静态编译:exe和dll各1份_crtheap 
1. 
Ctest8Dlg *pDlg = (Ctest8Dlg *)p; 

obj.m_str=_T("xx"); //这种操作本身不安全 

比较好的方法是SendMessage以自定义消息的形式通知pDlg来修改m_str的值。绝对不会崩溃。 

SendMessage(pDlg->GetSafeHwnd(),自定义消息编号,参数1,参数2); 


2.为什么要崩溃?我倒不理解了。 
楼主指针概念有问题吧。 
问题1: 
因为AfxBeginThread内部已经Attach了一次主界面 
C/C++ code12345678        CWnd threadWnd;....        // thread inherits app's main window if not already set        CWinApp* pApp = AfxGetApp();...…… 

你好,老师 

问题2 待会验证回复你 

问题1 

我似乎没有在帖子里提到用AfxBeginThread吧,最初我是用它创建线程,灵机一动,换成CreateThread 

原因是这个函数是 mfc提供的,怕其不崩溃,所以换成win32的,创建线程后,依然崩溃。 

所以你对其的回复,可能不是很精准。 

代码为: CreateThread(NULL,0,ThreadProc,this,0,NULL); 

线程代码为: 

DWORD WINAPI Ctest8Dlg:: ThreadProc(LPVOID p) 

Ctest8Dlg* pthis=(Ctest8Dlg*)FromHandle((HWND)p); 
pthis->m_str=CString(_T("hello")); 
pthis->UpdateData(FALSE); 

/*Ctest8Dlg obj; 
HWND hWnd=(HWND)p; 

obj.Attach(hWnd); 

obj.m_str=_T("xx"); 
obj.UpdateData(FALSE); 
obj.Detach();*/ 
return 0; 


线程函数,我知道发送消息其实是可以解决的,但是不选择发送消息的路线,是因为验证 

采用attach或者 FromHandle的法子如何解决。 

这是第一个帖子的目的。 

谢谢大家。 

问题1: 
因为AfxBeginThread内部已经Attach了一次主界面 
C/C++ code12345678        CWnd threadWnd;....        // thread inherits app's main window if not already set        CWinApp* pApp = AfxGetApp();...…… 

问题2:4中情形我都验证了,却是如你所说的。 

顺便问一句,,困惑很久的一个小技巧 

从vs2008开始,所有的工程里有个工程属性叫:  
mfc的使用,有3个选项: 

使用标准windows库 
mfc共享 
mfc静态 

非mfc工程,默认是mfc共享,     mfc静态自然是你说的 静态编译了 

那么使用标准windows库是什么东西? 

比如:我想写一个超级简单的控制台程序,然后发布出去,选择哪一个? 

此时,是不是有些无奈? 多了一个window标准库。               

如果是我,我选择静态mfc。 

实在不知道widnows 标准库是静态的,还是动态等。 

老师,你有空,可以帮我看看,谢谢 

1. 
Ctest8Dlg *pDlg = (Ctest8Dlg *)p; 

obj.m_str=_T("xx"); //这种操作本身不安全 

比较好的方法是SendMessage以自定义消息的形式通知pDlg来修改m_str的值。绝对不会崩溃。 

SendMessage(pDlg->GetSafeHwnd(),自定义消息编号,参数1,参数2); 


2…… 

看我的回复,我已经验证,却是崩溃了
MFC规定了一个线程仅仅能访问它所创建的MFC对象。 
为了阻止多个线程并发的访问同一个MFC对象,MFC对象和WIndows对象之间有一个一一对应的关系。这种关系以映射的形式保存在创建线程的当前模块的模块-线程状态信息中。当一个线程使用某个MFC对象指针P时,ASSERT_VALID(p)将验证当前线程的当前模块是否有Windows句柄和p对应,即是否创建了p所指的Windows对象,验证失败导致ASSERT断言中断程序的执行,如果一个线程要使用其它线程的Windows对象,则必须传递Windows对象的句柄,不能传递MFC对象的指针。 
http://support.microsoft.com/kb/147578/zh-cn?wa=wsignin1.0
MFC规定了一个线程仅仅能访问它所创建的MFC对象。 
为了阻止多个线程并发的访问同一个MFC对象,MFC对象和WIndows对象之间有一个一一对应的关系。这种关系以映射的形式保存在创建线程的当前模块的模块-线程状态信息中。当一个线程使用某个MFC对象指针P时,ASSERT_VALID(p)将验证当前线程的当前模块是否有Windows句柄和p对应,即是否创建了p所指的Wi…… 

老师你好,对于这个知识点,我早有所闻, 

只是不知道为什么mfc要有这么个规定 

mfc一共引入了4个状态:模块状态,线程状态, 模块-线程状态, 

进程状态。 

前3个状态似乎是为 mfc的tls服务的, 

理解起来也比较费劲。 



DWORD WINAPI Ctest8Dlg:: ThreadProc(LPVOID p) 
 { 
 Ctest8Dlg* pthis=(Ctest8Dlg*)FromHandle((HWND)p); 
 pthis->m_str=CString(_T("hello")); 
 pthis->UpdateData(FALSE); 
  
/*Ctest8Dlg obj; 
 HWND hWnd=(HWND)p; 
  
obj.Attach(hWnd); 
  
obj.m_str=_T("xx"); 
 obj.UpdateData(FALSE); 
 obj.Detach();*/ 
 return 0; 
 } 
  
我已经按照这个法子了,依然会有崩溃。 

崩溃在m_str赋值这里,原因不详! 

原因是FromHandle这个函数返回的是CWnd*指针,但是你将它强制转化成了Ctest8Dlg*指针,这样本身就是很危险的,而你后面又访问了Ctest8Dlg类自己定义的成员变量(而这个成员变量在CWnd类中并没有定义它),所以这里就错了。
原因是FromHandle这个函数返回的是CWnd*指针,但是你将它强制转化成了Ctest8Dlg*指针,这样本身就是很危险的,而你后面又访问了Ctest8Dlg类自己定义的成员变量(而这个成员变量在CWnd类中并没有定义它),所以这里就错了。 


有些道理, 

CWnd的派生类过多,而且其本身也有基类,那么最终拍摄各类Ctest8dlg* 所指向的内存就不是爷爷基类 
Cwnd的地址了,所以这里就错误了。 

如此说来, 

这个法子是个鸡肋了, 

对任何CWnd的派生类来说,都比较危险,除非用该指针去访问 

CWnd的成员函数才没事情, 否则只要访问派生类CDialog 或者Ctest8dlg里的 

自己增加的函数或者成员,都会出错。 

引用 10 楼 VisualEleven 的回复:原因是FromHandle这个函数返回的是CWnd*指针,但是你将它强制转化成了Ctest8Dlg*指针,这样本身就是很危险的,而你后面又访问了Ctest8Dlg类自己定义的成员变量(而这个成员变量在CWnd类中并没有定义它),所以这里就错了。 


有些道理, 

CWnd的派生类过多,而且其本身也有基类,那么最终…… 

CWnd* pthis=FromHandle((HWND)p); 
//pthis->m_str=CString(_T("hello")); 

if(pthis) 

pthis->SetWindowText(_T("xx")); 

setwindowtext 崩溃。 

说明这个法子,太危险了, 很多东西搞不定。 

指针我调试过了,是有值的。 


鸡肋法子啊,都不知道CWnd的哪一个成员函数可以使用了。 
引用 10 楼 VisualEleven 的回复:原因是FromHandle这个函数返回的是CWnd*指针,但是你将它强制转化成了Ctest8Dlg*指针,这样本身就是很危险的,而你后面又访问了Ctest8Dlg类自己定义的成员变量(而这个成员变量在CWnd类中并没有定义它),所以这里就错了。 


有些道理, 

CWnd的派生类过多,而且其本身也有基类,那么最终…… 
访问函数没有关系,只要该函数中没有访问该类中自己定义的成员变量即可 

this = 0x002cfdc4 {Ctest8Dlg hWnd=0x00680342} 

pthis = 0x001a9eac {CWnd hWnd=0x00680342} 

这是我做的实验, 在线程里获得CWnd指针是最后那个, CXXdlg指针是上的那个。 

可见,FromHandle获得的指针是祖父类的。并非孙类的。 

那么自然就不能使用祖父类的指针去调用非祖父类的函数了,对吧。 

除非动态绑定技术。 

这个从c++语法的角度分析,我觉得没有问题吧。 

顺便纠正12楼,12楼是错误的,  

原因在于,我传参数的时候,不小心没有把句柄,从线程里传进去。  

是我的失误
引用 3 楼 stjay 的回复:问题1: 
因为AfxBeginThread内部已经Attach了一次主界面 
C/C++ code12345678        CWnd threadWnd;....        // thread inherits app's main window if not already set        CWinApp* pApp …… 




同时跟正第5楼的实验 

和 
15楼一样, 线程传参数失误了, 代码是没有问题的。 

感谢大家的热心解答。 



引用 3 楼 stjay 的回复:问题1: 
因为AfxBeginThread内部已经Attach了一次主界面 
C/C++ code12345678        CWnd threadWnd;....        // thread inherits app's main window if not already set        CWinApp* pApp …… 


剩下第6楼的问题 

windows标准库是动态的,还是静态编译的? 
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
m_pMainWnd是MFC中的一个全局指针变量
映射窗口句柄对象
在MFC中从一个线程工作函数中向窗口发送消息(this指针的妙用) ---不好用呢
VC++中有关句柄和指针及其转换
VC|MFC学习笔记六: 几个小知识(I)--全域函数,数据类型,CWnd和HWnd等
子类化和超类化区别(转自--眼见为实(2):介绍Windows的窗口、消息、子类化和超类化...
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服