原文地址:http://hi.baidu.com/rootlife/blog/item/2f37e354ad8cdc5bd10906be.html

Introduction
当我们在用MFC编程的时候,我们经常用到AfxGetApp()来获得当前的CWinApp的Instance。看看MFC的源代码中AfxGetApp()的实现,你会发现AfxGetApp()的实现并不像一般情况下面那样直接:

_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(AfxGetStaticModuleState())

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
常见的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:
         AFX_INLINE TYPE* GetData()
         {
                TYPE* pData = (TYPE*)CProcessLocalObject::GetData(&CreateObject);

ENSURE(pData != NULL);

return pData;
         }
         AFX_INLINE TYPE* GetDataNA()

{ return (TYPE*)m_pObject; }

AFX_INLINE operator TYPE*()

{ return GetData(); }

AFX_INLINE TYPE* operator->()

{ return GetData(); }

// Implementation

public:
         static CNoTrackObject* AFXAPI CreateObject()

{ 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)
         {
                AfxLockGlobals(CRIT_PROCESSLOCAL);
                TRY
                {
                       if (m_pObject == NULL)

m_pObject = (*pfnCreateObject)();
                }
                CATCH_ALL(e)
                {
                       AfxUnlockGlobals(CRIT_PROCESSLOCAL);
                       THROW_LAST();
                }

END_CATCH_ALL

AfxUnlockGlobals(CRIT_PROCESSLOCAL);
         }
         return m_pObject;
}

3.        Thread State
和Process State类似,Thread State和某个线程绑定起来,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)
         {
                if (_afxThreadData == NULL)

{
                       _afxThreadData = new(__afxThreadData) CThreadSlotData;

ENSURE(_afxThreadData != NULL);
                }
                m_nSlot = _afxThreadData->AllocSlot();

ENSURE(m_nSlot != 0);
         }
         CNoTrackObject* pValue = static_cast<CNoTrackObject*>(_afxThreadData->GetThreadValue(m_nSlot));

if (pValue == NULL)
         {
                // allocate zero-init object

pValue = (*pfnCreateObject)();

// set tls data to newly created object

_afxThreadData->SetValue(m_nSlot, pValue);

ASSERT(_afxThreadData->GetThreadValue(m_nSlot) == pValue);
         }
         return 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()

{
         _AFX_THREAD_STATE *pState =_afxThreadState.GetData();

ENSURE(pState != NULL);

return pState;
}

_AFX_THREAD_STATE中保存着下列信息:

1.       当前的m_pModuleState,每个线程都知道它当前的Module State,这个信息被用来获得当前的Module State,AfxGetModuleState正是这么做的:

AFX_MODULE_STATE* AFXAPI AfxGetModuleState()
{
         _AFX_THREAD_STATE* pState = _afxThreadState;

ENSURE(pState);

AFX_MODULE_STATE* pResult;

if (pState->m_pModuleState != NULL)
         {
                // thread state's module state serves as override

pResult = pState->m_pModuleState;
         }
         else
         {
                // otherwise, use global app state
                pResult = _afxBaseModuleState.GetData();
         }

ENSURE(pResult != NULL);

return pResult;
}

2.       之前的m_pModuleState,用来保存之前的Module State,用于Module State切换,可参考AFX_MANAGE_STATE

3.       其他信息,具体可以参考_AFX_THREAD_STATE的定义

4.        Module State
Module State保存着和Module相关的状态信息。Module是Windows的术语,代表任何一个可执行的代码文件, EXE和DLL都是Module的一种。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的定义如下:

// AFX_MODULE_STATE (global data for a module)

class AFX_MODULE_STATE : public CNoTrackObject
{

public:

#ifdef _AFXDLL
         AFX_MODULE_STATE(BOOL bDLL, WNDPROC pfnAfxWndProc, DWORD dwVersion,

BOOL bSystem = FALSE);
#else
         explicit AFX_MODULE_STATE(BOOL bDLL);

#endif
         ~AFX_MODULE_STATE();

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
{
public:
         _AFX_DLL_MODULE_STATE() : AFX_MODULE_STATE(TRUE, AfxWndProcDllStatic, _MFC_VER)

{ }
};

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()

{
         _AFX_THREAD_STATE* pState = _afxThreadState;

ENSURE(pState);

AFX_MODULE_STATE* pResult;

if (pState->m_pModuleState != NULL)
         {
                // thread state's module state serves as override

pResult = pState->m_pModuleState;
         }
         else
         {
                // otherwise, use global app state

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()
{
         AFX_MODULE_STATE* pModuleState = &afxModuleState;

return pModuleState;
}

#else

AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState()
{
         AFX_MODULE_STATE* pModuleState = AfxGetModuleState();

return pModuleState;
}
#endif

3.       AfxGetAppModuleState

AfxGetAppModuleState是最简单的,直接返回_afxBaseModuleState:

AFX_MODULE_STATE* AFXAPI AfxGetAppModuleState()

{
         return _afxBaseModuleState.GetData();
}

从上面的讨论可以看出,当前处于那个MFC Module的状态之中,返回的就是那个MFC Module所相关联的CWinApp对象。如果你有多个Module都是动态链接到MFC DLL的话,那么AfxGetAppModuleState返回的总是同一个CWinApp。

5.        AFX_MANAGE_STATE
AFX_MANAGE_STATE的作用切换到指定的Module State,当出了作用域的时候将Module State恢复到原来的值。是在不同的Module State之中切换,原因有2:

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())


注意这里使用的是AfxGetStaticModuleState,而非AfxGetModuleState。原因是在DLL项目中,AfxGetStaticModuleState返回的是DLL本身的Module State,而AfxGetModuleState则是返回当前线程相关的Module State,由于一般DLL输出的函数是被其他Module调用,那么大部分情况下当前线程的Module State都是错误的,所以必须得使用DLL本身的Module State。

AFX_MANAGE_STATE只是一个宏,如下:

struct AFX_MAINTAIN_STATE2
{
         explicit AFX_MAINTAIN_STATE2(AFX_MODULE_STATE* pModuleState) throw();

~AFX_MAINTAIN_STATE2();
protected:

#ifdef _AFXDLL
         AFX_MODULE_STATE* m_pPrevModuleState;

_AFX_THREAD_STATE* m_pThreadState;
#endif
         ULONG_PTR m_ulActCtxCookie;

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()
{
#ifdef _AFXDLL

m_pThreadState = _afxThreadState.GetData();

ASSERT(m_pThreadState);

if(m_pThreadState)
         {
                m_pPrevModuleState = m_pThreadState->m_pModuleState;

m_pThreadState->m_pModuleState = pNewState;
         }
         else
         {
                // This is a very bad state; we have no good way to report the error at this moment

// since exceptions from here are not expected

m_pPrevModuleState=NULL;

m_pThreadState=NULL;
         }
#endif
         if (AfxGetAmbientActCtx() &&
                pNewState->m_hActCtx != INVALID_HANDLE_VALUE)
         {
                m_bValidActCtxCookie = AfxActivateActCtx(pNewState->m_hActCtx, &m_ulActCtxCookie);
         }
         else
         {
                m_bValidActCtxCookie = FALSE;
         }
}

然后在析构函数的时候将其恢复回来:

// AFX_MAINTAIN_STATE2 functions

_AFXWIN_INLINE AFX_MAINTAIN_STATE2::~AFX_MAINTAIN_STATE2()
{
#ifdef _AFXDLL

// Not a good place to report errors here, so just be safe

if(m_pThreadState)
         {
                m_pThreadState->m_pModuleState = m_pPrevModuleState;
         }
#endif
         if (m_bValidActCtxCookie)
         {
                BOOL bRet;

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的模块状态相关推荐

  1. VS2019 MFC DLL共享动态链接库(MFC 常规库)封装例程非模态调用 MFC 常规库[二]

    [ 前言]          网上用很多关于MFC 共享DLL的简介,此处不在叙述.实际应用中发现"共享MFC DLL的规则DLL"是在编写基于MFC的DLL程序时,编译后该DLL ...

  2. 蓝牙模块HC-05 AT指令使用以及两个蓝牙模块的配对

    蓝牙模块用的就是某宝常见的蓝牙模块,首先要将蓝牙模块进入AT模式,按住蓝牙按键后再通电就会进入蓝牙的AT模式,进入AT模式后蓝牙led慢闪,然后就可以利用TTL或者J-Link与串口助手给模块发送AT ...

  3. 一图看懂 aiohttp 模块:基于 asyncio 的异步HTTP网络库, 资料整理+笔记(大全)

    本文由 大侠(AhcaoZhu)原创,转载请声明. 链接: https://blog.csdn.net/Ahcao2008 一图看懂 aiohttp 模块:基于 asyncio 的异步HTTP网络库, ...

  4. et200s模块接线图讲解_西门子ET200S 1 STEP 步进模块使用入门.doc

    西门子ET200S 1 STEP 步进模块使用入门 ET200S 1 STEP 步进模块使用入门 Getting Started of ET200S 1 STEP 5V/204KHzGetting S ...

  5. php 授权模块,PHP免授权功能模块:智慧农场小程序 1.8.9后台模块前端小程序源码模块插件...

    功能模块:智慧农场小程序 1.8.9后台模块+前端小程序源码 内容介绍: 版本号:1.8.9 – 普通版注意:1.本次更新需要重新上传小程序 2.本次更新内容较多,请注意备份–新增小程序自己添加收货地 ...

  6. uiswitchbutton 点击不改变状态_Redux 包教包会(一):解救 React 状态危机

    前端应用的状态管理日益复杂.随着大前端时代的到来,前端愈来愈注重处理逻辑,而不只是专注 UI 层面的改进,而以 React 为代表的前端框架的出现,大大简化了我们编写 UI 界面的复杂度.虽然 Rea ...

  7. 对于python来说、一个模块就是一个文件-PYTHON中的包和模块

    为了更加友好的对python代码进行组织管理,python中出现了包和模块的概念 类似生活中整理我们的物品一样,将代码按照不同的功能进行整理整合,可以很大程度的提升代码可读性和代码质量,方便在项目中进 ...

  8. 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 ...

  9. Python第二十二天 stat模块 os.chmod方法 os.stat方法 pwd grp模块

    Python第二十二天   stat模块  os.chmod方法  os.stat方法  pwd  grp模块 stat模块描述了os.stat(filename)返回的文件属性列表中各值的意义,根据 ...

最新文章

  1. 少一些计较多_人与人之间最舒服的关系:低期待,少索取,不苛求
  2. Linux 中挖矿病毒处理过程
  3. en55032最新标准下载_大型设备塔吊安装维保、安全检查及案例,94页PPT下载!
  4. 车智汇模式系统技术开发数据
  5. =在 java中怎么表示_在Java中各种类型运算符的介绍与其基本使用方式(有具体使用示例)...
  6. u-boot的patch文件制作
  7. Sublime Text 3 快捷键总结(详细版本)
  8. gVim取消自动备份
  9. 20200426:186周周赛(上)(leetcode5392-5394)
  10. java代码获取系统时间相差8小时
  11. 方差分析ANOVA、单因素方差分析、协变量方差分析ANCOVA、重复测量方差分析、双因素方差分析( two-way ANOVA)、多元方差分析MANOVA、多元协方差分析MANCOVA
  12. C#使用欧姆龙PLC的Fins协议读写PLC地址(基本封装)
  13. 展锐Camera open failure log解析程序
  14. ​最新淘宝商品详情接口API(稳定低成本)
  15. html css before,详解 CSS 属性 - :before :after
  16. python用pow计算负数的平方根_pow(x, 0.5)能够计算x的平方根,计算负数的平方根将产生:...
  17. Java 常用工具类 Collections 源码分析
  18. VMware Cloud Foundry –见解
  19. 运维自动化之系统部署
  20. 论文阅读Cars Can’t Fly up in the Sky

热门文章

  1. This application requires Java Runtime Environment
  2. KERMIT,XMODEM,YMODEM,ZMODEM传输协议小结
  3. js 取html自定义属性,JS操作html中的自定义属性
  4. 设计模式--策略模式--Java实现
  5. 区分 Protobuf 中缺失值和默认值
  6. flex white-space: nowrap,撑大盒子问题
  7. java8使用stream操作集合类,如何使用bigDicemal计算工资?
  8. 【实战】docker-compose 编排 多个docker 组成一个集群并做负载
  9. 魅族显示无法连接到服务器,魅族连接电脑无法识别怎么办_魅族手机usb无法连接电脑的解决方法...
  10. 软件开发中Alpha、Beta、RC、GA版本的含义