`
congfeng02
  • 浏览: 200094 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态

阅读更多

MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态

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相关的一些信息。这次就写到这里。

分享到:
评论

相关推荐

    MFC 图标和文本共存的按钮

    在Windows编程领域,MFC(Microsoft Foundation Classes)是一个强大的库,它为开发Windows应用程序提供了C++接口。在MFC中,我们经常使用CButton类来创建标准的按钮控件。然而,CButton类默认只支持显示纯文本或...

    MFC.rar_MSChart tooltip mfc _tooltip_显示控件

    在Microsoft Foundation Class (MFC)库中,使用ToolTip控件是一种常见的增强用户界面的方法,它可以在用户将鼠标悬停在...通过不断实践和学习,你将能够熟练掌握在MFC环境中使用ToolTip和状态条来提升用户体验的技术。

    MFC控件Custom Control使用源码

    MFC为开发者提供了便捷的方式来实现这一目标,使得我们可以利用C++的强大特性和面向对象编程的便利性,构建自己的控件。下面将详细介绍如何在MFC中使用自定义控件,以及相关的关键知识点。 首先,自定义控件的创建...

    Afx全局函数及MFC常见数据类型.rar_MFC_全局函数

    在Microsoft Foundation Class (MFC)库中,Afx全局函数和常见的数据类型是开发者进行Windows应用程序开发时不可或缺的工具。MFC是一个C++类库,它封装了Windows API,为开发者提供了一种更加面向对象的方式来编写...

    加密密文明文还原程序

    MFC加密程序 CMy1Dlg::CMy1Dlg(CWnd* pParent /*=NULL*/) : CDialog(CMy1Dlg::IDD, pParent) { //{{AFX_DATA_INIT(CMy1Dlg) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT ...

    MFC的程序框架剖析

    创建框架类和视图类,从这里也可以看出MFC体系结构中文档、框架、视图“三位一体”的模式,在这一个函数中同时创建三个类;再会调用pDocument-&gt;OnNewDocument();因此就会进入到子类的文档类中的pDocument-&gt;...

    MFC深入浅出:从MFC设计到MFC编程

    同时,MFC的国际化和本地化机制,如CWinApp::LoadString和AfxGetApp()-&gt;SetCurrentResource,使应用程序能够适应不同语言环境。 总的来说,《MFC深入浅出:从MFC设计到MFC编程》涵盖了从基础概念到高级技巧的广泛...

    MFC指针的使用

    可以使用`GetDescendantWindow()`方法找到状态栏和工具栏,指定相应的窗口ID(AFX_IDW_STATUS_BAR 和 AFX_IDW_TOOLBAR): ```cpp CStatusBar *pStatusBar = (CStatusBar *)AfxGetMainWnd()-&gt;GetDescendantWindow...

    vc++ADOX创建数据库

    m_hIcon = AfxGetApp()-&gt;LoadIcon(IDR_MAINFRAME); } void CADOXCreateDatabaseDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CADOXCreateDatabaseDlg) DDX_Text...

    深入浅出MFC

    8. Afx宏和AFX_MODULE_STATE:MFC使用Afx宏进行内存管理和模块状态管理,如AfxGetApp()用于获取当前应用程序的CWinApp实例。 9. 打印和打印预览:MFC通过CView和CPreviewView类支持打印和打印预览功能,使得开发者...

    mfc实现简单计算器

    ### MFC实现简单计算器 #### 一、项目背景与概述 MFC(Microsoft Foundation Classes)是微软提供的一个C++类库,主要用于简化Windows程序开发。本文档将介绍如何使用MFC来开发一个简单的计算器应用程序。该计算器...

    MFC应用程序中指针的应用

    在MFC(Microsoft Foundation Class)应用程序开发中,指针的使用是至关重要的,尤其是在涉及到对象间的交互和数据传递时。MFC是微软提供的一套面向对象的类库,用于简化Windows应用程序的开发,它基于C++语言。在这...

    MFC制作的MP3

    使用MFC制作MP3打开vc6.0,建立如图所示mfc工程文件 选择基于对话框的确定 删除所有空间,建立如图所示对话框 属性如下: 播放 IDC_open; 添加 IDC_fileopen; 暂停 IDC_pause; 删除 IDC_del; 停止 IDC_stop...

    mfc 显示任务栏图标

    本教程将详细讲解如何在MFC应用中显示任务栏图标并实现图标闪烁功能,这对于理解和增强Windows桌面应用的用户体验至关重要。 首先,我们需要创建一个MFC应用程序,通常使用Visual Studio IDE来完成。在新建项目时...

    mfc--非模式对话框

    本文将深入探讨如何在Win7环境下,利用Visual Studio 2010和MFC技术将一个基于模式对话框的应用程序改写为非模式对话框。 **一、模式对话框与非模式对话框** 1. **模式对话框**:当模式对话框打开时,用户必须先...

    MFC实现Edit输入限制(只允许输入数字,负号和小数点)

    在MFC(Microsoft Foundation Classes)框架中,我们经常需要对用户界面控件进行自定义,以满足特定的输入要求。本教程将详细讲解如何实现一个只允许用户输入数字、负号和小数点的CEdit控件。这个功能在很多需要进行...

    MFC全屏显示

    3. **状态变量定义**:在主框架窗口类(CMainFrame)中定义状态变量来记录全屏模式是否开启、工具栏和状态栏是否可见,以及窗口的原始位置和尺寸。 ```cpp class CMainFrame : public CFrameWnd { public: ...

    基于MFC的数字Edit类(只能输入数字)

    在Windows应用程序开发中,Microsoft Foundation Class (MFC)库是一个强大的工具,它为开发者提供了封装Windows API的类库。在MFC中,`CEdit`类是用于处理标准Windows编辑框(Edit Control)的,而有时我们需要对这...

    MFC文档视图指针相获取

    在Microsoft Foundation Classes (MFC)框架中,文档视图架构是一种设计模式,它将数据(文档)与用户界面(视图)分离,使得程序能够更好地管理数据和显示。本文将详细讲解如何在MFC中获取不同组件的指针,这对于...

Global site tag (gtag.js) - Google Analytics