MFC 六大机制

一、入口函数机制(程序启动机制)

语言总结:

3.1 构造theApp 对象CWinApp::CWinApp()

1)将&theApp 保存到当前程序模块状态信息中

2)将&theApp 保存到当前程序线程状态信息中

3)AfxGetApp/AfxGetThread - 返回&theApp

3.2 程序流程(WinMain)

1)利用AfxGetApp/AfxGetThread 获取&theApp

2)利用&theApp 调用应用程序类成员虚函数

InitApplication(初始化)

3)利用&theApp 调用应用程序类成员虚函数

InitInstance(创建、显示窗口)

4)利用&theApp 调用应用程序类成员虚函数

Run(消息循环)

4.1 利用&theApp 调用应用程序类成员虚函数

OnIdle(空闲处理)

4.2 利用&theApp 调用应用程序类成员虚函数

ExitInstance(善后处理)

伪代码:

AFX_MODULE_STATE aaa;//当前程序模块状态信息

AFX_MODULE_THREAD_STATE bbb;//当前程序线程状态信息

//构造theApp 对象

CWinApp::CWinApp(...)

{

AFX_MODULE_STATE* pModuleState=AfxGetModuleState()

//获取全局变量&aaa

AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;

2

//获取全局变量&bbb

pThreadState->m_pCurrentWinThread = this;

//将&theApp 保存到全局变量bbb 的一个成员中

AfxGetThread()

{

AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();

//获取全局变量&bbb

CWinThread* pThread = pState->m_pCurrentWinThread;

//获取&theApp

return pThread;

}

pModuleState->m_pCurrentWinApp = this;

//将&theApp 保存到全局变量aaa 的一个成员中

AfxGetApp()

{

return AfxGetModuleState()->m_pCurrentWinApp;

}

}

****************************************

//体会是不是theApp 对象指导流程。

WinMain(...)

{

AfxWinMain(...)

{

CWinThread* pThread = AfxGetThread();

CWinApp* pApp = AfxGetApp();//以上两句获取&theApp

pApp->InitApplication();

//利用theApp 对象调用CWinApp 类成员虚函数(初始化)

pThread->InitInstance();

//利用theApp 对象调用应用程序类的成员虚函数

pThread->Run()

//利用theApp 对象调用应用程序类的成员虚函数(消息循环)

{

CWinThread::Run()//函数内部this 为&theApp

{

while(没有消息)

{

this->OnIdle(...);//应用程序类成员虚函数(空闲处理)

}

do

{

if( GetMessage 抓到WM_QUIT)

return ExitInstance();

//应用程序类成员虚函数(善后处理)

3

}while(...)

}

}

}

}

二、窗口创建机制

语言总结:

1 加载菜单

2 调用CreateEx 注册窗口类,创建窗口

2.1 调用PreCreateWindow 设计并注册窗口类

WNDCLASS wndcls;

....

wndcls.lpfnWndProc = DefWindowProc;

最终调用win32 API 函数RegisterClass 注册了一个窗口类

2.2 调用AfxHookWindowCreate 函数

1)将pFrame(自己new 的框架类对象地址)保存到当前程序

线程信息中。

2)利用::SetWindowsHookEx 函数在程序中埋下一个钩子,

类型为WH_CBT.

2.3 利用win32 API 函数::CreateWindowEx 创建窗口,此函数

一旦执行触发钩子处理函数

3 钩子处理函数

3.1 将自己new 框架类对象地址和窗口句柄建立一对一的

绑定关系。

m_hWnd = hWnd - 通过pFrame 可以获取窗口句柄

m_permanentMap[hWnd] = pFrame

通过窗口句柄可以获取pFrame

3.2 利用win32 API 函数::SetWindowLong 将窗口处理函数更

改为AfxWndProc(真正窗口处理函数)

伪代码:

_afxWndFrameOrView==="AfxFrameOrView42sd"

CMyFrameWnd *pFrame = new CMyFrameWnd;

pFrame->Create( NULL, "MFCCreate" )//函数内部this 为pFrame

{

//加载菜单

CreateEx( ..,NULL..)//函数内部this 为pFrame

{

4

CREATESTRUCT cs;

cs.lpszClass = NULL;//下边将更改

......

cs.hInstance = AfxGetInstanceHandle();

PreCreateWindow(cs)

{

AfxDeferRegisterClass(...)

{

WNDCLASS wndcls;

....

wndcls.lpfnWndProc = DefWindowProc;//下边更改

_AfxRegisterWithIcon(&wndcls,"AfxFrameOrView42sd")

{

&wndcls->lpszClassName = "AfxFrameOrView42sd";

AfxRegisterClass(&wndcls)

{

::RegisterClass(&wndcls);

}

}

}

cs.lpszClass = "AfxFrameOrView42sd";

}

AfxHookWindowCreate(pFrame)//参数为自己new 的框架类对象地址

{

_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();

//获取全局变量&ccc(当前程序线程信息)

::SetWindowsHookEx(WH_CBT...);

//在程序中埋下一个类型为WH_CBT 的钩子

pThreadState->m_pWndInit = pFrame;

//将自己new 的框架类对象地址pFrame 保存到ccc 的一个成员中

}

::CreateWindowEx(...);//此函数一旦执行WM_CREATE 消息出现

//立即被钩子勾到钩子处理函数中。

}

}

*********************************************************

//钩子处理函数

_AfxCbtFilterHook(...wParam..)

{

_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();

//获取全局变量&ccc

CWnd* pWndInit = pThreadState->m_pWndInit;

//从ccc 中获取pFrame===pWndInit

HWND hWnd = (HWND)wParam;//获取窗口句柄

5

pWndInit->Attach(hWnd)//函数内部this 为pFrame===pWndInit

{

CHandleMap* pMap = afxMapHWND(TRUE)

{

AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();

//获取全局变量&bbb

pState->m_pmapHWND = new CHandleMap(...);

//将new 出来的映射类对象地址保存bbb 的一个成员中

return pState->m_pmapHWND;

}

pMap->SetPermanent(m_hWnd = hWnd, pFrame)

//函数内部this 为pMap(映射类对象地址)

{

m_permanentMap[hWnd] = pFrame;

}

}

WNDPROC afxWndProc = AfxGetAfxWndProc();

//获取AfxWndProc 函数的地址

::SetWindowLong(hWnd, GWL_WNDPROC,(DWORD)afxWndProc);

//将窗口处理函数更改为AfxWndProc(真正的窗口处理函数)

}

***************************************************

以WM_CREATE 消息为例

AfxWndProc(....)

{

CWnd* pWnd = CWnd::FromHandlePermanent(hWnd)

{

CHandleMap* pMap = afxMapHWND()

{

AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();

//获取&bbb

return pState->m_pmapHWND;

//返回上边保存的映射类对象地址

}

pWnd = (CWnd*)pMap->LookupPermanent(hWnd)

//函数内部this 为pMap(映射类对象地址)

{

return m_permanentMap[hWnd];//获取pFrame

}

}

AfxCallWndProc(pWnd...)//参数的pWnd===pFrame

{

pWnd->WindowProc(...)

{

6

//回到自己代码

}

}

}

三、消息映射机制

语言总结:

1 消息映射机制的使用

1)类必须从CCmdTarget 类派生

2)类内必须添加声明宏DECLARE_MESSAGE_MAP()

3) 类外必须添加实现宏

BEGIN_MESSAGE_MAP( theClass, baseClass )

END_MESSAGE_MAP( )

2 消息映射机制的实现

2.1 数据结构

struct AFX_MSGMAP_ENTRY(静态数组每个元素类型)

{

UINT nMessage; // 消息ID

UINT nCode; // 通知码

UINT nID; // 命令ID/控件ID

UINT nLastID; // 最后一个控件ID

UINT nSig; // 消息处理函数的类型

AFX_PMSG pfn; // 消息处理函数的地址(指针)

};

struct AFX_MSGMAP(静态变量类型)

{

const AFX_MSGMAP* pBaseMap;//

const AFX_MSGMAP_ENTRY* lpEntries;//

};

2.2 宏展开

见代码

2.3 宏展开各部分的作用

_messageEntries[] - 静态数组

每个元素保存消息ID 对应的消息处理函数的指针

messageMap - 静态变量

负责获取父类静态变量地址(连接链表)

负责获取相应类的静态数组首地址

GetMessageMap() - 虚函数

获取本类静态变量地址(获取链表头节点)

2.4 消息映射机制的执行过程

7

1)利用框架类对象(pFrame)调用GetMessageMap 获取链表

头节点(本类静态变量地址)pMessageMap

2)利用pMessageMap 的第二个成员获取相应类的静态数

组首地址,在数组中查找消息ID 对应处理函数指针

如果找到了4,如果没找到执行3

3)利用pMessageMap 的第一个成员获取父类静态变量地址

如果为NULL,结束查找,如果不为NULL 执行2

4)利用找到的这个数组元素的第六个成员

(消息处理函数地址)完成消息的处理。

二MFC 消息的分类

1 windows 标准例如:键盘/鼠标/定时器....

ON_WM_XXX

2 自定义消息

#define WM_MYMESSAGE WM_USER+n

ON_MESSAGE

3 命令消息(WM_COMMAND)

ON_COMMAND

ON_COMMAND_RANGE( 起始ID,终止ID,处理函数)

伪代码:

//以WM_CREATE 消息为例(想着点WM_COMMAND/WM_PAINT)

AfxWndProc(...)

{

CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);

//获取和hWnd 绑定在一起框架类对象地址(pFrame===pWnd)

AfxCallWndProc(pWnd..)//参数pWnd===pFrame

{

pWnd->WindowProc(...)//函数内部this 为pFrame===pWnd

{

OnWndMsg(...)//函数内部this 为pFrame===pWnd

{

const AFX_MSGMAP* pMessageMap = GetMessageMap();

//获取链表头节点(本类静态变量地址)

AFX_MSGMAP_ENTRY* lpEntry;

for (; pMessageMap != NULL;//遍历链表找东西

pMessageMap = pMessageMap->pBaseMap)

{

lpEntry = AfxFindMessageEntry

(pMessageMap->lpEntries,message...);

if( lpEntry != NULL )

{

goto LDispatch;

}

}

LDispatch:

8

union MessageMapFunctions mmf;

mmf.pfn = lpEntry->pfn;//获取&OnCreate

int nSig = lpEntry->nSig;//AfxSig_lwl

switch (nSig)

{

case AfxSig_lwl:

lResult = (this->*mmf.pfn_lwl)(wParam, lParam);

break;

.....

}

}

}

}

}

四、运行时类信息(CRuntimeClass)

1 运行时类信息机制作用

程序在执行过程中可以获取对象相关类的信息

(对象是否属于该类)

2 运行时类信息机制使用

2.1 类必须从CObject 派生

2.2 类内必须添加声明宏DECLARE_DYNAMIC

2.3 类外必须添加实现宏IMPLEMENT_DYNAMIC

CObject::IsKindOf - 可以判断对象是否属于某个类

3 运行时类信息机制的实现

3.1 数据结构

struct CRuntimeClass (静态变量类型)

{

LPCSTR m_lpszClassName;//类名称

int m_nObjectSize; //类大小sizeof

UINT m_wSchema; //类的版本0XFFFF

CObject* (PASCAL* m_pfnCreateObject)();

//动态创建机制使用,运行时类信息机制为NULL

CRuntimeClass* m_pBaseClass;

//父类静态变量地址(用于连接链表)

CRuntimeClass* m_pNextClass;//为NULL

};

3.2 宏展开的代码

见代码

3.3 宏展开各部分的作用

9

classCDog - 静态变量

保存类的相关信息例如:类大小/名称/版本..

GetRuntimeClass() - 虚函数

获取本类静态变量地址(链表头节点)

3.4 IsKindOf 函数的执行过程

利用本类对象yellowdog 调用虚函数GetRuntimeClass 获取

本类静态变量地址(链表头节点)

利用本类静态变量地址与目标进行比对如果相等证明对象

属于该类,如果不相等循环比较

循环比较过程中只要有一次相等证明对象属于该类,循环

结束一次都不相等证明对象不属于该类

**************

RUNTIME_CLASS( theClass ) - 括号内的类的静态变量地址

(&theClass::classtheClass)

伪代码:

五、动态创建(本质是new 对象)

通常的编程方式,程序员使用系统的类创建对象,调用成员函数完成

相关功能。有了动态创建,系统的代码可以创建程序员定义的类的

对象,换言之,底层代码可以创建上层类的对象。

语言总结:

1 作用

在不知道类名情况下,将类对象创建出来。

2 使用

2.1 类必须从CObject 类派生

2.2 类内必须添加声明宏DECLARE_DYNCREATE

2.3 类外必须添加实现宏IMPLEMENT_DYNCREATE

CRuntimeClass::CreateObject - 动态创建类对象

3 实现

3.1 区别(和运行时类信息机制比较)

classCDog - 静态变量

第四个成员不再为NULL,保存了一个静态函数的地址

函数的区别

多了一个静态函数CreateObject

3.2 作用

CreateObject() - 静态函数

new 了一个CDog 类的对象并返回对象地址

classCDog - 静态变量

将新增加静态函数CreateObject 的地址保存在第四个

10

成员中

GetRuntimeClass() - 虚函数

获取本类(CDog)的静态变量地址(链表头节点)

3.3 CRuntimeClass::CreateObject 执行过程

1)利用本类静态变量(classCDog)的第四个成员

2)调用成员中保存新增加静态函数(CDog::CreateObject)

完成对象的创建(这个函数内部new 了一个CDog 类的

对象,并返回对象地址)

伪代码:

//动态创建机制

CObject* pObject=RUNTIME_CLASS(CDog)->CreateObject(){

// 函数内部this 指针为&classCDog --- 链表头结点

CObject* pObject = NULL;

pObject = (*m_pfnCreateObject)() //调用静态变量第四个成员

--->CDog::CreateObject

{

return new CDog;

}

六、对象的序列化

二序列化

引入CArchive 类,带来的好处:1 可以设置文件读写的缓冲区大小

2 读写各种基本数据类型,不需要类型转换。

1 概念

将数据以二进制流的方式写入到文件或者从文件中读取。

2 使用

2.1 打开或者新建文件

CFile::Open

2.2 文件读写

2.2.1 定义CArchive 对象

2.2.2 具体的数据读写

CArchive::operator >> "读取"

CArchive::operator << "写入"

2.2.3 关闭CArchive 对象

CArchive::Close

2.3 关闭文件

CFile::Close

三对象的序列化(MFC 的第六个机制)

1 概念

11

序列化对象-将对象的类信息以及对象的成员变量以二进制流的

方式依次写入到文件的过程。

(运行时类信息是序列化的必要条件)

反序列化对象-从文件中读取类的信息,创建对象,然后读取文件

中的成员变量初始化新建的对象的过程。

(动态创建是序列化的必要条件)

2 使用

2.1 定义支持序列化的类

2.1.1 派生自CObject 类

2.1.2 添加序列化的声明宏和实现宏

2.1.3 重写虚函数Serilize(),在函数中,完成类的数据成员的

序列化

2.2 读写对象

读写对象时传入的参数是对象的地址,步骤与读写基本类型

的数据一样。

3 原理-- 伪代码

3.1 展开宏的结构体

struct AFX_CLASSINIT

{

//构造函数

AFX_CLASSINIT(CRuntimeClass* pNewClass)

{

AfxClassInit(pNewClass);

{

AFX_MODULE_STATE* pModuleState = AfxGetModuleState();

AfxLockGlobals(CRIT_RUNTIMECLASSLIST);

//将当前的运行时类信息保存到应用程序的m_classList 链表中

pModuleState->m_classList.AddHead(pNewClass);

AfxUnlockGlobals(CRIT_RUNTIMECLASSLIST);

}

}

};

_init_CStudent 是一个全局的结构体变量,类型是结构体

AFX_CLASSINIT。进入_tmain()函数之前,将当前类的

运行时类信息保存到模块状态信息的m_classList 链表中

3.2 写入对象的过程

ar.WriteObject(pOb);

{

//获取当前类的运行时类信息

CRuntimeClass* pClassRef = pOb->GetRuntimeClass();

//将对象的类的信息写入到文件

WriteClass(pClassRef);

{

12

pClassRef->Store(*this);

{

//类名称长度

WORD nLen = (WORD)lstrlenA(m_lpszClassName);

//依次将版本、类名称长度写入文件

ar << (WORD)m_wSchema << nLen;

//将类名称写入文件

ar.Write(m_lpszClassName, nLen*sizeof(char));

}

}

//由于虚函数机制,调用CStudent::Serialize()函数,

//将类的成员变量依次写入到文件

((CObject*)pOb)->Serialize(*this);

{

CObject::Serialize(ar);

if (ar.IsStoring())

{

ar<<m_strName<<m_nAge;

}

...

}

}

3.3 读取对象的过程

ar.ReadObject(RUNTIME_CLASS(CStudent));

{

//得到当前类的运行时类信息

ReadClass(pClassRefRequested, &nSchema, &obTag);

{

CRuntimeClass::Load(*this, &nSchema);

{

ar >> wTemp;

ar >> nLen;

ar.Read(szClassName, nLen*sizeof(char));

szClassName[nLen] = '\0';

//在链表中根据类的名称循环查找得到运行时类信息

for (pClass = pModuleState->m_classList; pClass != NULL;

pClass = pClass->m_pNextClass)

{

if (lstrcmpA(szClassName, pClass->m_lpszClassName) == 0)

{

AfxUnlockGlobals(CRIT_RUNTIMECLASSLIST);

return pClass;

}

}

13

}

}

//动态创建对象

pOb = pClassRef->CreateObject();

//使用从文件中读取的类的成员初始化新建的对象

pOb->Serialize(*this);

{

CObject::Serialize(ar);

{

...

else

{

ar>>m_strName>>m_nAge;

}

}

}

}


本文出自 “日知其所无” 博客,谢绝转载!

你可能感兴趣的:(虚函数,应用程序,信息,启动机)