一、入口函数机制(程序启动机制)
语言总结:
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;
}
}
}
}
本文出自 “日知其所无” 博客,谢绝转载!