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标准库是动态的,还是静态编译的?