DLL资源切换.doc_第1页
DLL资源切换.doc_第2页
DLL资源切换.doc_第3页
DLL资源切换.doc_第4页
DLL资源切换.doc_第5页
免费预览已结束,剩余1页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

AFX_MANAGE_STATE(AfxGetStaticModuleState() 以前写MFC的DLL的时候,总会在自动生成的代码框架里看到提示,需要在每一个输出的函数开始添加上AFX_MANAGE_STATE (AfxGetStaticModuleState()。一直不明白这样做的含义,也一直没有这样做,而且代码也工作得好好的,所以感觉这好像一句废话。最近的项目中,需要在DLL里使用MFC生成界面,这才发现一旦资源放在不同的动态库里,而且还和多线程搅和在一起的时候,事情就变得异常的复杂,以前对MFC的一知半解已经不足与应付了。程序莫名的崩溃,莫名的ASSERT,资源怎样也装载不起来,为什么呢?每次,总是尝试着,在每一个线程的开始,把AFX_MANAGE_STATE(AfxGetStaticModuleState()添加上去,或者在某些地方用 AfxSetResourceHandler()一把,然后问题就解决了,但是不是很明白到底是怎么回事,总感觉这种解决办法让人很不安心,仿佛在下一秒问题又会突然冒出来。前天,这个问题终于发挥到了极致,任我花费了好几个小时,怎样的尝试都不能成功,在项目的关键时候发生这种事情,让我暗暗发誓以后再也不用MFC了。正像很多的电影情节一样,事情最后还是得到了解决,这次我决定不能再这么算了,一定要把这个事情理解得明明白白。在这里,我遇到的问题就是,如何让DLL里的界面代码使用该DLL的资源(Resource),如何在工作线程里加载有IE控件的对话框?我问同事,他们是如何实现DLL资源切换的?AFX_MANAGE_STATE(AfxGetStaticModuleState()这就是他们的答案,一如微软的推荐,原来就是这么简单啊!让我们来看看,这句代码到底做了什么?#define AFX_MANAGE_STATE(p) AFX_MAINTAIN_STATE2 _ctlState(p);AFX_MAINTAIN_STATE2:AFX_MAINTAIN_STATE2(AFX_MODULE_STATE* pNewState) m_pThreadState = _afxThreadState; m_pPrevModuleState = m_pThreadState-m_pModuleState; m_pThreadState-m_pModuleState = pNewState;_AFXWIN_INLINE AFX_MAINTAIN_STATE2:AFX_MAINTAIN_STATE2() m_pThreadState-m_pModuleState = m_pPrevModuleState; 原来,就是定义一个局部的对象,利用其构造和析构函数在函数的入口和函数的出口进行State状态的切换,我猜AfxGetStaticModuleState()一定是获取当前代码所在DLL的State。果然,请看static _AFX_DLL_MODULE_STATE afxModuleState;AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState() AFX_MODULE_STATE* pModuleState = &afxModuleState; return pModuleState;class _AFX_DLL_MODULE_STATE : public AFX_MODULE_STATE/ AFX_MODULE_STATE (global data for a module)class AFX_MODULE_STATE : public CNoTrackObject. CWinApp* m_pCurrentWinApp; HINSTANCE m_hCurrentInstanceHandle; HINSTANCE m_hCurrentResourceHandle; LPCTSTR m_lpszCurrentAppName; BYTE m_bDLL; / TRUE if module is a DLL, FALSE if it is an EXE. COccManager* m_pOccManager;.这里不得不说,MFC把很多的数据都堆放在这里,搞得很复杂,结构性非常的差。afxModuleState是dll的静态成员,自然可以被同样的dll里的代码所访问,但是何时初始化的?extern CBOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/). AfxWinInit(hInstance, NULL, _T(), 0);.BOOL AFXAPI AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) ASSERT(hPrevInstance = NULL); / handle critical errors and avoid Windows message boxes SetErrorMode(SetErrorMode(0) | SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); / set resource handles AFX_MODULE_STATE* pModuleState = AfxGetModuleState(); pModuleState-m_hCurrentInstanceHandle = hInstance; pModuleState-m_hCurrentResourceHandle = hInstance;.原来在DLL的入口函数,用该DLL的hInstance初始化了该结构。到这时候,我们还是不明白,为什么要进行资源切换?前面开始的_afxThreadState到底是什么?好像跟Thread有关系,到底是什么呢?THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)#define THREAD_LOCAL(class_name, ident_name) AFX_DATADEF CThreadLocal ident_name;templateclass CThreadLocal : public CThreadLocalObject再 往下跟踪,发现其实代码越发生涩难懂,但是基本的功能就是访问当前此行代码的线程的私有数据。所谓线程的私有数据,就是说,不同的线程执行同样的一段代码,得到的数据可能是不同的。这才想起来,MFC的很多句柄啦,都是保存在全局的Map里的,而且放在线程的私有数据区里,所以跨线程传递MFC对象是很 不安全的。但是,MFC为什么要这么做呢?这个问题,到目前为止,我还是搞不明白。还是回到开始的代码,资源切换到底是如何进行的?int CDialog:DoModal(). HINSTANCE hInst = AfxGetResourceHandle(); if (m_lpszTemplateName != NULL) hInst = AfxFindResourceHandle(m_lpszTemplateName, RT_DIALOG); HRSRC hResource = :FindResource(hInst, m_lpszTemplateName, RT_DIALOG); hDialogTemplate = LoadResource(hInst, hResource);._AFXWIN_INLINE HINSTANCE AFXAPI AfxGetResourceHandle() ASSERT(afxCurrentResourceHandle != NULL); return afxCurrentResourceHandle; #define afxCurrentResourceHandle AfxGetModuleState()-m_hCurrentResourceHandleAFX_MODULE_STATE* AFXAPI AfxGetModuleState() _AFX_THREAD_STATE* pState = _afxThreadState; AFX_MODULE_STATE* pResult; if (pState-m_pModuleState != NULL) / thread states module state serves as override pResult = pState-m_pModuleState; else / otherwise, use global app state pResult = _afxBaseModuleState.GetData(); ASSERT(pResult != NULL); return pResult;原 来MFC的对话框装载资源是通过获取当前线程对应的ModuleState保存的ResourceHandler来装载资源的。所以,DLL里的代码,需 要在函数的入口,首先把当前执行线程的ModuleState换成该Dll的State,这样才能装载该dll的资源!这时候,我突然明白过来,为什么需 要要依赖线程的私有数据来保存ModuleState,其实确切的说是传递!-这其实是因为CDialog是存放在另一个DLL里的,比如 MFC40.dll,如果以共享模式连接MFC库的话。而用户自己编写的CDialog的子类并不放在CDialog同样的Dll里,他们如何来传递这个 资源句柄呢?两种解决办法:1,利用参数传递。2,存放在一个公共的地方。前者需要增加参数,显得很麻烦,Win32的API好像就是这样实现的吧?后 者,需要确定这个公共地方在何处?这让人想起来,建立一个公共的动态库?由主程序的提供?再多说一句,J2EE里有一个容器的概念(COM+好像也有,不 知道.NET是如何的),组件都是生存在容器里,这时候我们就可以设想把该数据存放在容器里。不管怎样,MFC的实现就是放在线程的私有数据区,不需要公 共的动态库,也不需要麻烦主程序,它自己就搞定了!它自以为很好的解决方式,很完美,却引发了我们的一系列的问题,特别是不明白就里的人。关 于资源装载,问题似乎已经解决了,但是还有一点点小麻烦就是,我实现的dll不是以普通的输出函数进行输出的,而是输出类,我可不想在每一个类的成员函数里添加AFX_MANAGE_STATE(AfxGetStaticModuleState()。怎么办呢?既然已经知道了资源切换的原理,我们添加两个输出函数,分别对应AFX_MAINTAIN_STATE2的构造和析构函数,在类的使用前后调用,就可以了。或者,分别放在类的构造和析构函数里。又或者,就声明为成员变量。无论怎样,需要保证的一点就是资源的切换要正确嵌套,不可交叉这种情况在不同的DLL之间交叉调用的时候会发生。好 了,现在DLL里的资源可以正确调用了,但是在当Dialog上包含有IE控件的时候,我们还是失败了,为什么呢?我知道对于ActiveX控件, Dialog需要做一些特殊的处理,AfxEnableControlContainer(),我也知道,要使用COM,需要CoInitialize (),但是我一直没有想过需要两个一起用才能把IE弄出来,但是最后就是这样的。奇怪的是,如果不是在工作线程里,根本不需要CoInitialize (),就能装载IE控件的,这个暂时就先不管了。PROCESS_LOCAL(COccManager, _afxOccManager)void AFX_CDECL AfxEnableControlContainer(COccManager* pOccManager) if (pOccManager = NULL) afxOccManager = _afxOccManager.GetData(); else afxOccManager = pOccManager;#define afxOccManager AfxGetModuleState()-m_pOccManager这 样看来,这个_afxOccManager应该是属于整个进程的,整个进程只有一个,就在那个定义它的dll里。但是,你需要把该对象(或者创建

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论