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