MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态
原文地址:http://hi.baidu.com/rootlife/blog/item/2f37e354ad8cdc5bd10906be.html Introduction _AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp() { return afxCurrentWinApp; } #define afxCurrentWinApp AfxGetModuleState()->m_pCurrentWinApp AfxGetApp()调用的是AfxGetModuleState(),该函数返回一个AFX_MODULE_STATE的指针,其中的一个成员保存着当前的CWinApp的指针。可AfxGetModuleState()的作用又是什么呢? 此外,当我们在开发MFC DLL程序的时候,我们会在每个输出的DLL函数前面加上一句AFX_MANAGE_STATE: void SomeMFCDllFunction() … AFX_MANAGE_STATE又是起什么作用呢?从字面上看来,它是Manage某种State,而AfxGetStaticModuleState又是获得State的,那么State究竟是什么呢? 在MFC中,States用来保存某种相关的状态信息,分为下面几类: 1. Process State,和某个单独的进程绑定起来 2. Thread State,和某个单独的线程绑定 3. Module State,和Module相关 前两种State和一般的全局变量十分类似,只是根据需求的不同被绑定于不同的进程/线程,如多线程支持等。而Module State本身比较特别,Module State根据情况的不同,可以是全局,线程,或者进程相关的State,并且可以根据要求快速切换。 2. Process State 1. _AFX_WIN_STATE 2. _AFX_DB_STATE 3. _AFX_DEBUG_STATE 4. _AFX_SOCK_STATE 5. …… 从字面上面可以很容易猜出这些状态的用处。 MFC通过下面的宏来定义Process State: #define PROCESS_LOCAL(class_name, ident_name) \ AFX_COMDAT CProcessLocal<class_name> ident_name; #define EXTERN_PROCESS_LOCAL(class_name, ident_name) \ extern CProcessLocal<class_name> ident_name; PROCESS_LOCAL用CProcessLocal模板类定义了一个CProcessLocal<class_name>的一个实例作为状态变量,而EXTERN_PROCESS_LOCAL则使在头文件中声明此状态变量。CProcessLocal的定义如下: class AFX_NOVTABLE CProcessLocalObject public: // Attributes CNoTrackObject* GetData(CNoTrackObject* (AFXAPI* pfnCreateObject)()); // Implementation CNoTrackObject* volatile m_pObject; ~CProcessLocalObject(); template<class TYPE> class CProcessLocal : public CProcessLocalObject // Attributes public: ENSURE(pData != NULL); return pData; { return (TYPE*)m_pObject; } AFX_INLINE operator TYPE*() { return GetData(); } AFX_INLINE TYPE* operator->() { return GetData(); } // Implementation public: { return new TYPE; } }; CProcessLocal的作用只是一个Wrapper,Hold一个TYPE*的指针,一旦用户调用GetData来获得这个指针,GetData会首先判断该指针是否为空,如果为空,则创建一个新的实例保存起来,否则返回已有的指针。前提条件是,TYPE必须从CNoTrackObject继承。任何从CNoTrackObject继承的类都拥有自己的new/delete,这样此对象便不会被Debug的内存分配系统所跟踪而误判为Leak。 CNoTrackObject* CProcessLocalObject::GetData( CNoTrackObject* (AFXAPI* pfnCreateObject)()) if (m_pObject == NULL) m_pObject = (*pfnCreateObject)(); END_CATCH_ALL AfxUnlockGlobals(CRIT_PROCESSLOCAL); 3. Thread State 1. _AFX_THREAD_STATE 2. _AFXCTL_AMBIENT_CACHE 同样的,Thread State是被THREAD_LOCAL和EXTERN_THREAD_LOCAL定义,也有CThreadLocal和CThreadLocalObject来Hold住Thread State的指针。CThreadLocal和CProcessLocal的实现方式不太一样,CThreadLocal利用TLS(Thread Local Storage)来保存指针,而不是用成员变量。简单来说,Thread Local Storage是Windows支持的功能,可以在任意线程中保存多个DWORD数据,每个这样的DWORD数据所占的位置称之为Slot,分配数据需要分配一个Slot,获得和修改数据CThreadLocalObject::GetData的实现如下: CNoTrackObject* CThreadLocalObject::GetData( CNoTrackObject* (AFXAPI* pfnCreateObject)()) ENSURE(pfnCreateObject); if (m_nSlot == 0) { ENSURE(_afxThreadData != NULL); ENSURE(m_nSlot != 0); if (pValue == NULL) pValue = (*pfnCreateObject)(); // set tls data to newly created object _afxThreadData->SetValue(m_nSlot, pValue); ASSERT(_afxThreadData->GetThreadValue(m_nSlot) == pValue); CThreadLocalObject::GetData首先判断m_nSlot,如果m_nSlot == 0,说明该Thread State未曾分配,GetData函数将会使用_afxThreadData->AllocSlot函数分配一个新的TLS的Slot,保存在m_nSlot之中,然后调用GetThreadValue检查pValue是否为NULL,如果是,则创建一个新的对象然后调用SetValue把pValue设置到该Slot之中。_afxThreadData的类型为CThreadSlotData,是对TLS API的一个简单的封装。 _AFX_THREAD_STATE是一个很常用的Thread State,每个Thread,都会有自己的一份_AFX_THREAD_STATE。MFC提供了一个函数AfxGetThreadState来获得当前进程的Thread State,如果当前的线程还没有Thread State,该函数会创建一个新的Thread State。 _AFX_THREAD_STATE* AFXAPI AfxGetThreadState() { ENSURE(pState != NULL); return pState; _AFX_THREAD_STATE中保存着下列信息: 1. 当前的m_pModuleState,每个线程都知道它当前的Module State,这个信息被用来获得当前的Module State,AfxGetModuleState正是这么做的: AFX_MODULE_STATE* AFXAPI AfxGetModuleState() ENSURE(pState); AFX_MODULE_STATE* pResult; if (pState->m_pModuleState != NULL) pResult = pState->m_pModuleState; ENSURE(pResult != NULL); return pResult; 2. 之前的m_pModuleState,用来保存之前的Module State,用于Module State切换,可参考AFX_MANAGE_STATE 3. 其他信息,具体可以参考_AFX_THREAD_STATE的定义 4. Module State 1. AFX_MODULE_STATE,保存MODULE的信息,是_AFX_BASE_MODULE_STATE和_AFX_DLL_MODULE_STATE的基类 2. _AFX_BASE_MODULE_STATE,保存MFC Module的状态信息,没有定义其他的成员 3. _AFX_DLL_MODULE_STATE,保存DLL的状态信息,没有定义其他的成员 4. AFX_MODULE_THREAD_STATE,保存主线程的有关状态信息,虽然AFX_MODULE_THREAD_STATE是保存的线程的状态信息,但是它只保存Module的主线程的状态信息,所以可以看作是Module State的一种。 这些Module State保存了MFC中的大量重要信息: 1. CWinApp指针 2. 实例句柄 3. 资源Module的句柄 4. 句柄表 5. OLE相关信息 6. 窗口过程 7. Activation Context 8. …… 4.1 AFX_MODULE_STATE // AFX_MODULE_STATE (global data for a module) class AFX_MODULE_STATE : public CNoTrackObject public: #ifdef _AFXDLL BOOL bSystem = FALSE); #endif CWinApp* m_pCurrentWinApp; HINSTANCE m_hCurrentInstanceHandle; HINSTANCE m_hCurrentResourceHandle; LPCTSTR m_lpszCurrentAppName; // …… 其他成员,从略 }; 可以看到: 1. AFX_MODULE_STATE从CNoTrackObject继承。CNoTrackObject定义了自己的new/delete保证自己不会被各种调试版本的new/delete来Track,以免自己被错误的当作Leak。 2. AFX_MODULE_STATE在DLL和非DLL(也就是EXE)的情况下具有不同的构造函数(和成员) 3. AFX_MODULE_STATE在成员中保存了一些和Module相关的重要信息 实际上,AFX_MODULE_STATE并没有被直接使用,而是作为_AFX_BASE_MODULE_STATE和_AFX_DLL_MODULE_STATE的基类: _AFX_BASE_MODULE_STATE被用于Module,其定义如下: class _AFX_BASE_MODULE_STATE : public AFX_MODULE_STATE { public: #ifdef _AFXDLL _AFX_BASE_MODULE_STATE() : AFX_MODULE_STATE(TRUE, AfxWndProcBase, _MFC_VER) #else _AFX_BASE_MODULE_STATE() : AFX_MODULE_STATE(TRUE) #endif { } }; PROCESS_LOCAL(_AFX_BASE_MODULE_STATE, _afxBaseModuleState) _AFX_DLL_MODULE_STATE和_AFX_BASE_MODULE_STATE类似,只是仅用于DLL: class _AFX_DLL_MODULE_STATE : public AFX_MODULE_STATE { } static _AFX_DLL_MODULE_STATE afxModuleState; 这两个class都没有定义额外的成员,比较简单,只是传入到基类AFX_MODULE_STATE的参数不同。此外,他们定义的方式不太一样,前者使用的是PROCESS_LOCAL宏,定义了一个变量_afxBaseModuleState。后者只是简单的定义了一个static变量afxModuleState。 下面这些函数可以用来获得Module的State: 1. AfxGetModuleState AfxGetModuleState首先获得_afxThreadState的m_pModuleState,如果当前的Thread State的m_pModuleState返回NULL,说明当前的Thread State没有正确的初始化(通常的原因是创建线程的时候调用的是CreateThread函数而非AfxBeginThread),则使用_afxBaseModuleState。 AFX_MODULE_STATE* AFXAPI AfxGetModuleState() { ENSURE(pState); AFX_MODULE_STATE* pResult; if (pState->m_pModuleState != NULL) pResult = pState->m_pModuleState; pResult = _afxBaseModuleState.GetData(); ENSURE(pResult != NULL); return pResult; _afxBaseModuleState是用PROCESS_LOCAL定义的: PROCESS_LOCAL(_AFX_BASE_MODULE_STATE, _afxBaseModuleState) 它代表整个MFC Module的State。当你的程序是动态链接到MFC DLL的时候,该State只有一份。如果你的程序是静态链接到MFC的话,有几个模块(EXE/DLL)静态链接到MFC,MFC的代码就有几份,那么_afxBaseModuleState也就有几份。 2. AfxGetStaticModuleState AfxGetStaticModuleState在不同的Project下面有着不同的行为:在DLL项目中,AfxGetSaticModuleState返回afxModuleState,也就是定义好的_AFX_DLL_MODULE_STATE,而在非DLL项目中,AfxGetStaticModuleState直接调用AfxGetModuleState。可以看到,在DLL的情况下,必须使用AfxGetStaticModuleState才可以获得DLL本身的Module State。 #ifdef _AFXDLL static _AFX_DLL_MODULE_STATE afxModuleState; AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState() return pModuleState; #else AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState() return pModuleState; 3. AfxGetAppModuleState AfxGetAppModuleState是最简单的,直接返回_afxBaseModuleState: AFX_MODULE_STATE* AFXAPI AfxGetAppModuleState() { 从上面的讨论可以看出,当前处于那个MFC Module的状态之中,返回的就是那个MFC Module所相关联的CWinApp对象。如果你有多个Module都是动态链接到MFC DLL的话,那么AfxGetAppModuleState返回的总是同一个CWinApp。 5. AFX_MANAGE_STATE 1. 在不同的MFC DLL和MFC EXE的Module State之间切换,保持正确的AFX_MODULE_STATE,最常见的问题是在DLL输出的函数之中无法获得DLL本身相关的资源,这就是没有正确维护Module State的原因造成的,因为当前Resource DLL的句柄就保存在Module State之中。 2. 切换Activation Context,不同的Module必然有着不同的Activation Context,需要切换。这是属于Side By Side的内容,以后我会专门写一篇文章来讲述Side By Side和manifest的相关信息。 一般的用法如下: void SomeMFCDllFunction() { AFX_MANAGE_STATE(AfxGetStaticModuleState()) … AFX_MANAGE_STATE只是一个宏,如下: struct AFX_MAINTAIN_STATE2 ~AFX_MAINTAIN_STATE2(); #ifdef _AFXDLL _AFX_THREAD_STATE* m_pThreadState; BOOL m_bValidActCtxCookie; #define AFX_MANAGE_STATE_NO_INIT_MANAGED(p) AFX_MAINTAIN_STATE2 _ctlState(p); #define AFX_MANAGE_STATE(p) _AfxInitManaged(); AFX_MANAGE_STATE_NO_INIT_MANAGED(p) 可以看到AFX_MANAGE_STATE声明了一个栈上的局部变量_ctrlState,类型为AFX_MAINTAIN_STATE2。这是一个很常用的Pattern,AFX_MAINTAIN_STATE2在构造函数的时候会将当前的Module State切换为参数中指定的Module State: AFX_MAINTAIN_STATE2::AFX_MAINTAIN_STATE2(AFX_MODULE_STATE* pNewState) throw() m_pThreadState = _afxThreadState.GetData(); ASSERT(m_pThreadState); if(m_pThreadState) m_pThreadState->m_pModuleState = pNewState; // since exceptions from here are not expected m_pPrevModuleState=NULL; m_pThreadState=NULL; 然后在析构函数的时候将其恢复回来: // AFX_MAINTAIN_STATE2 functions _AFXWIN_INLINE AFX_MAINTAIN_STATE2::~AFX_MAINTAIN_STATE2() // Not a good place to report errors here, so just be safe if(m_pThreadState) bRet = AfxDeactivateActCtx(0, m_ulActCtxCookie); ASSERT(bRet == TRUE); 可以看到,AFX_MAINTAIN_STATE2将当前_afxThreadState在m_pThreadState中存起来,然后将所指向的Module State保存在m_pPrevModuleState中。在析构函数中,则使用保存起来的m_pPrevModuleState恢复到m_pThreadState的Module State。除了保存恢复Module state之外,AFX_MAINTAIN_STATE2也会在切换Activation Context。这个Activation Context被用来查找Side By Side Assemblies,我以后会专门写一篇文章讲述Side By Side和Manifest相关的一些信息。这次就写到这里。 |
转载于:https://blog.51cto.com/qsjming/455702
MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态相关推荐
- VS2019 MFC DLL共享动态链接库(MFC 常规库)封装例程非模态调用 MFC 常规库[二]
[ 前言] 网上用很多关于MFC 共享DLL的简介,此处不在叙述.实际应用中发现"共享MFC DLL的规则DLL"是在编写基于MFC的DLL程序时,编译后该DLL ...
- 蓝牙模块HC-05 AT指令使用以及两个蓝牙模块的配对
蓝牙模块用的就是某宝常见的蓝牙模块,首先要将蓝牙模块进入AT模式,按住蓝牙按键后再通电就会进入蓝牙的AT模式,进入AT模式后蓝牙led慢闪,然后就可以利用TTL或者J-Link与串口助手给模块发送AT ...
- 一图看懂 aiohttp 模块:基于 asyncio 的异步HTTP网络库, 资料整理+笔记(大全)
本文由 大侠(AhcaoZhu)原创,转载请声明. 链接: https://blog.csdn.net/Ahcao2008 一图看懂 aiohttp 模块:基于 asyncio 的异步HTTP网络库, ...
- et200s模块接线图讲解_西门子ET200S 1 STEP 步进模块使用入门.doc
西门子ET200S 1 STEP 步进模块使用入门 ET200S 1 STEP 步进模块使用入门 Getting Started of ET200S 1 STEP 5V/204KHzGetting S ...
- php 授权模块,PHP免授权功能模块:智慧农场小程序 1.8.9后台模块前端小程序源码模块插件...
功能模块:智慧农场小程序 1.8.9后台模块+前端小程序源码 内容介绍: 版本号:1.8.9 – 普通版注意:1.本次更新需要重新上传小程序 2.本次更新内容较多,请注意备份–新增小程序自己添加收货地 ...
- uiswitchbutton 点击不改变状态_Redux 包教包会(一):解救 React 状态危机
前端应用的状态管理日益复杂.随着大前端时代的到来,前端愈来愈注重处理逻辑,而不只是专注 UI 层面的改进,而以 React 为代表的前端框架的出现,大大简化了我们编写 UI 界面的复杂度.虽然 Rea ...
- 对于python来说、一个模块就是一个文件-PYTHON中的包和模块
为了更加友好的对python代码进行组织管理,python中出现了包和模块的概念 类似生活中整理我们的物品一样,将代码按照不同的功能进行整理整合,可以很大程度的提升代码可读性和代码质量,方便在项目中进 ...
- Building MFC application with /MD[d] (CRT dll version) requires MFC shared dll version 错误解决
Building MFC application with /MD[d] (CRT dll version) requires MFC shared dll version 错误解决 今天在VS201 ...
- Python第二十二天 stat模块 os.chmod方法 os.stat方法 pwd grp模块
Python第二十二天 stat模块 os.chmod方法 os.stat方法 pwd grp模块 stat模块描述了os.stat(filename)返回的文件属性列表中各值的意义,根据 ...
最新文章
- 少一些计较多_人与人之间最舒服的关系:低期待,少索取,不苛求
- Linux 中挖矿病毒处理过程
- en55032最新标准下载_大型设备塔吊安装维保、安全检查及案例,94页PPT下载!
- 车智汇模式系统技术开发数据
- =在 java中怎么表示_在Java中各种类型运算符的介绍与其基本使用方式(有具体使用示例)...
- u-boot的patch文件制作
- Sublime Text 3 快捷键总结(详细版本)
- gVim取消自动备份
- 20200426:186周周赛(上)(leetcode5392-5394)
- java代码获取系统时间相差8小时
- 方差分析ANOVA、单因素方差分析、协变量方差分析ANCOVA、重复测量方差分析、双因素方差分析( two-way ANOVA)、多元方差分析MANOVA、多元协方差分析MANCOVA
- C#使用欧姆龙PLC的Fins协议读写PLC地址(基本封装)
- 展锐Camera open failure log解析程序
- ​最新淘宝商品详情接口API(稳定低成本)
- html css before,详解 CSS 属性 - :before :after
- python用pow计算负数的平方根_pow(x, 0.5)能够计算x的平方根,计算负数的平方根将产生:...
- Java 常用工具类 Collections 源码分析
- VMware Cloud Foundry –见解
- 运维自动化之系统部署
- 论文阅读Cars Can’t Fly up in the Sky
热门文章
- This application requires Java Runtime Environment
- KERMIT,XMODEM,YMODEM,ZMODEM传输协议小结
- js 取html自定义属性,JS操作html中的自定义属性
- 设计模式--策略模式--Java实现
- 区分 Protobuf 中缺失值和默认值
- flex white-space: nowrap,撑大盒子问题
- java8使用stream操作集合类,如何使用bigDicemal计算工资?
- 【实战】docker-compose 编排 多个docker 组成一个集群并做负载
- 魅族显示无法连接到服务器,魅族连接电脑无法识别怎么办_魅族手机usb无法连接电脑的解决方法...
- 软件开发中Alpha、Beta、RC、GA版本的含义