MFC基础技术,听了苏坤老师,李全福的视频 讲座--从实例学VC,学习总结web
MFC程序的初始化过程
2.1.1 _tWinMainwindows
从代码能够看到,该函数仅调用了AfxMain函数。
_tWinMain是被C/C++运行期函数调用的,这里暂很少讲,由于这部分很复杂。
2.1.2 AfxWinMain
看一下该函数代码,这里仅写主要的调用,将其它都省略掉。数组
2.1.3 AfxGetThread
这个函数返回一个CWinThread类型指针,这其实是一个全局对象的指针,该函数内部执行很是复杂,这里不讲。但能够知道,程序正式经过该指针管理线程的。
2.1.4 AfxGetApp
返回CWinApp对象指针,这也是一个全局对象指针,有理由相信,该对象就是theApp这个全局对象。
2.1.5 AfxWinInit
看一下该函数简化的代码:
// handle critical errors and avoid Windows message boxes
SetErrorMode(…);
// set resource handles
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
pModuleState->m_hCurrentInstanceHandle = hInstance;
pModuleState->m_hCurrentResourceHandle = hInstance;
pModuleState->CreateActivationContext();
// fill in the initial state for the application
CWinApp* pApp = AfxGetApp();
// Windows specific initialization (not done if no CWinApp)
pApp->m_hInstance = hInstance;
App->m_lpCmdLine = lpCmdLine;
pApp->m_nCmdShow = nCmdShow;
pApp->SetCurrentHandles();
// initialize thread specific data (for main thread)
AfxInitThread();
// Initialize CWnd::m_pfnNotifyWinEvent
HMODULE hModule = ::GetModuleHandle(_T("user32.dll"));
CWnd::m_pfnNotifyWinEvent =
(CWnd::PFNNOTIFYWINEVENT)::GetProcAddress(hModule, NotifyWinEvent");
从这个函数能够看出,该函数作了以下工做:
设置错误模式。
设置资源,这里先不作深刻学习,这里极可能设置一个模块句柄,而后程序默认从该模块中读取资源。
初始化CWinApp某些成员。
AfxInitThread为主线程安装了一个WH_MSGFILTER类型的挂钩,并指定了一个挂钩函数_AfxMsgFilterHook,当消息来自于dialog box, message box, menu, or scroll bar时,系统就会调用挂钩函数,这里暂不研究挂钩函数。
初始化类的CWnd静态函数指针m_pfnNotifyWinEvent,它指向模块user32.dll中的函数NotifyWinEvent,关于该函数参看msdn。
2.1.6 InitApplication
m_pDocManager = CDocManager::pStaticDocManager;
m_pDocManager->AddDocTemplate(NULL);
LoadSysPolicies();
该函数主要初始化了成员m_pDocManager。
LoadSysPolicies函数暂时不深刻学习。
2.1.7 InitInstance
这个函数就很少说了。
2.1.8 Run
下面是该函数的代码:
_AFX_THREAD_STATE* pState = AfxGetThreadState();数据结构
// for tracking the idle time state
BOOL bIdle = TRUE;
LONG lIdleCount = 0;app
// acquire and dispatch messages until a WM_QUIT message is received.
for (;;)
{
// phase1: check to see if we can do idle work
while (bIdle &&
!::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE))
{
// call OnIdle while in bIdle state
if (!OnIdle(lIdleCount++))
bIdle = FALSE; // assume "no idle" state
}函数
// phase2: pump messages while available
do
{
// pump message, but quit on WM_QUIT
if (!PumpMessage())
return ExitInstance();学习
// reset "no idle" state after pumping "normal" message
//if (IsIdleMessage(&m_msgCur))
if (IsIdleMessage(&(pState->m_msgCur)))
{
bIdle = TRUE;
lIdleCount = 0;
}字体
} while (::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE));
}
Run函数中的for是一个死循环,直到do…while循环中执行ExitInstance为止,这里不讨论这一点。
若是While循环要退出,必须知足两个条件之一:
PeekMessage在线程的消息循环中检查到了消息,取出,但不会见该消息从消息队列中删除。
OnIdle返回0值,这意味着没有空闲时间了,程序要作事了。
可见,这两个条件是不相同的,从这两个条件出发,还能够得出以下结论:
尽管PeekMessage函数没有从线程消息队列中取出任何消息,可是也没有空闲时间了。
尽管有空闲时间,但PeekMessage从线程消息队列中取到了消息。
这两个解释说明,空闲时间同线程消息队列为空没有必要的联系。
不过,一旦进入do…while循环,执行过PumpMessage函数以后,有一个函数调用IsIdleMessage,用以判断刚分发的那个消息是否可让程序处于空闲时间。在do…while循环结束以前,若是全部消息都不让程序处于空闲时间,那么函数OnIdle就不能执行,然而只要有一个,OnIdle函数酒能够执行(事实上这样的消息出现频率很大)。但为什么要这样设计呢?
最后,受到WM_QUIT消息时调用CWinApp::ExitInstance()作一些清理工做。
2.1.9 PumpMessage
看一下该函数所做的事情:
_AFX_THREAD_STATE *pState = AfxGetThreadState();ui
if (!::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL))
{
::TranslateMessage(&(pState->m_msgCur));
::DispatchMessage(&(pState->m_msgCur));
}
调用了GetMessage(经过其它函数),该函数不但从消息队列取消息,以后还将消息删除。而后分配该消息。
2.1.10 AfxWinMain函数InitFailure后的代码
InitFailure以后的代码如今暂不考虑。this
2.2 RTTI(运行时识别类)
运行时识类技术主要是在类中添加一个结构变量和一些成员函数,经过对象的指针能够找到对象的类名。
在MFC类库中,将该结构用链表连起来,就构成一个类别型录网,由于结构中含有类名,父类等信息,因此能够遍历链表寻找到本类或父类的信息(至少有名称),以此能够识别类。
2.2.1 CRuntimeClass定义
这里,取未定义_AFXDLL宏。
struct CRuntimeClass
{
// Attributes
LPCSTR m_lpszClassName;
int m_nObjectSize;
UINT m_wSchema; // schema number of the loaded class
CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class
CRuntimeClass* m_pBaseClass;
// Operations
CObject* CreateObject();
BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const;
// dynamic name lookup and creation
static CRuntimeClass* PASCAL FromName(LPCSTR lpszClassName);
static CRuntimeClass* PASCAL FromName(LPCWSTR lpszClassName);
static CObject* PASCAL CreateObject(LPCSTR lpszClassName);
static CObject* PASCAL CreateObject(LPCWSTR lpszClassName);
// Implementation
void Store(CArchive& ar) const;
static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum);
// CRuntimeClass objects linked together in simple list
CRuntimeClass* m_pNextClass; // linked list of registered classes
const AFX_CLASSINIT* m_pClassInit;
};
这该结构声明,下面会逐一说明每一个成员。
2.2.2 DECLARE_DYNAMIC
这个宏用于在一个类中添加同RTTI相关的成员,下面看这个宏的定义。
#define DECLARE_DYNAMIC(class_name) \
public: \
static const CRuntimeClass class##class_name; \
virtual CRuntimeClass* GetRuntimeClass() const; \
以DECLARE_DYNAMIC(CView) 为例,展开后获得:
class CView{
public:
static const CRuntimeClass classCView;//静态变量
virtual CRuntimeClass* GetRuntimeClass() const;//虚函数声明
… …
};
下面看看宏IMPLEMENT_DYNAMIC的实现。
2.2.3 IMPLEMENT_DYNAMIC
这个宏是对DECLARE_DYNAMIC声明内容的实现,下面是定义:
#define IMPLEMENT_DYNAMIC(class_name, base_class_name) \
IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL, NULL)
#define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew, class_init) \
AFX_COMDAT const CRuntimeClass class_name::class##class_name = { \
#class_name, sizeof(class class_name), wSchema, pfnNew, \
RUNTIME_CLASS(base_class_name), NULL, class_init }; \
CRuntimeClass* class_name::GetRuntimeClass() const \
{ return RUNTIME_CLASS(class_name); }
#define RUNTIME_CLASS(class_name) _RUNTIME_CLASS(class_name)
#define _RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))
以IMPLEMENT_DYNAMIC(CView, CWnd)为例,展开后以下:
AFX_COMDAT const CRuntimeClass CView::classCView = //初始化静态变量
{
"CView", // m_lpszClassName
sizeof(class CView), // m_nObjectSize
0xFFFF, // m_wSchema
NULL, // m_pfnCreateObject
((CRuntimeClass*)(&CWnd::classCWnd), // m_pBaseClass
NULL, // m_pNextClass
NULL // m_pClassInit
};
CRuntimeClass* CView::GetRuntimeClass() const
{
return ((CRuntimeClass*)(&CView::classCView);
}
2.2.4 IsKindOf和IsDerivedFrom
该函数定义在CObject类中,看一下它的定义(简化版):
BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const
{
// simple SI case
CRuntimeClass* pClassThis = GetRuntimeClass();
ENSURE(pClassThis);
return pClassThis->IsDerivedFrom(pClass);
}
下面是该函数的通常调用:
pView->IsKindOf(RUNTIME_CLASS(CView));
注意如下参数,该参数展开后以下:
((CRuntimeClass*)(&CView::classCView))
CView::class CView正是CView类的静态变量。
调用:
CRuntimeClass* pClassThis = GetRuntimeClass();
GetRuntimeClass从CObject继承下来的,它返回CView::class CView。
有意思的调用是pClassThis->IsDerivedFrom(pClass);
看看IsDerivedFrom的简化代码:
BOOL CRuntimeClass::IsDerivedFrom(const CRuntimeClass* pBaseClass) const
{
// simple SI case
const CRuntimeClass* pClassThis = this;
while (pClassThis != NULL)
{
if (pClassThis == pBaseClass)
return TRUE;
pClassThis = pClassThis->m_pBaseClass;
}
return FALSE; // walked to the top, no match
}
很显然,pClassThis就是当前对象所属类的CRuntimeClass静态成员的指针,pBaseClass就是参考类(当前对象是否属于该类)的CRuntimeClass静态成员的指针,经过这两个指针对比(是否相同),来判断对象是否属于参考类的对象。那么为什么不经过字符串对比(要知道CRuntimeClass有类的名称字符串)呢?
从while循环能够看出,pClassThis会顺着类型链表向继承类方向遍历,这说明一个对象也属于其继承类类型,而这一点是没法经过类名称字符串比较作到的。
2.3 (Dynamic Create)动态建立
尽管可以在运行时获得一个类的名称,但仍是没法经过该名称动态建立类的对象,必须再向其它办法。为此,就要利用CRuntimeClass的m_pfnCreateObject成员,下面是该成员的定义(参考RTTI):
CObject* (PASCAL* m_pfnCreateObject)();
可见,这是一个函数指针,它返回CObject*。
若是想一个类具备动态建立对象的功能,只须要实现一个函数,让该函数建立对象,而且让m_pfnCreateObject指向该函数便可。
DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE宏就是用来作这件事情的。
2.3.1 DECLARE_DYNCREATE
看一下该宏的定义:
#define DECLARE_DYNCREATE(class_name) \
DECLARE_DYNAMIC(class_name) \
static CObject* PASCAL CreateObject();
DECLARE_DYNAMIC参考RTTI部分。
可见,该宏也就是为类添加一个静态成员CreateObject();
如DECLARE_DYNCREATE(CWnd)展开后以下:
class CWnd{
public:
static const CRuntimeClass classCView;//静态变量
virtual CRuntimeClass* GetRuntimeClass() const;//虚函数声明
static CObject* PASCAL CreateObject();//静态成员函数
… …
};
2.3.2 IMPLEMENT_DYNCREATE
看一下该宏的定义:
#define IMPLEMENT_DYNCREATE(class_name, base_class_name) \
CObject* PASCAL class_name::CreateObject() \
{ return new class_name; } \
IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, \
class_name::CreateObject, NULL)
IMPLEMENT_RUNTIMECLASS参考RTTI的DECLARE_DYNAMIC。
如IMPLEMENT_DYNCREATE(CWnd, CCmdTarget)展开后以下:
AFX_COMDAT const CRuntimeClass CWnd::classCWnd = //初始化静态变量
{
"CWnd", // m_lpszClassName
sizeof(class CWnd), // m_nObjectSize
0xFFFF, // m_wSchema
CWnd::CreateObject, // m_pfnCreateObject
((CRuntimeClass*)(&CWnd::classCWnd), // m_pBaseClass
NULL, // m_pNextClass
NULL // m_pClassInit
};
CRuntimeClass* CWnd::GetRuntimeClass() const
{
return ((CRuntimeClass*)(&CWnd::classCWnd);
}
CObject* PASCAL CWnd::CreateObject() { return new CWnd;}
从这个宏也能够看出,拥有动态建立能力的类也必定有动态识别能力。
下面再看一下CRuntimeClass的实现(简化):
CObject* CRuntimeClass::CreateObject()
{
if (m_pfnCreateObject == NULL)
return NULL;
CObject* pObject = NULL;
TRY
{
pObject = (*m_pfnCreateObject)();
}
END_TRY
return pObject;
}
因此,只要可以找到一个类的CRuntimeClass静态成员,就能够动态建立该类的对象,固然,要找到该成员是不费吹灰之力的。
2.3.3 使用
例如动态建立一窗口对象:
CRuntimeClass* prc = RUNTIME_CLASS(CWnd);
CWnd* pWnd = (CWnd*)prc->CreateObject();
BOOL b = pWnd->IsKindOf(RUNTIME_CLASS(CWnd));
delete pWnd;
2.4 Persistence(永久保存机制)
暂时不看。
2.5 Serialize(数据读写)
暂时不看。
2.6 Message Mapping(消息映射)
对于同消息处理有关的类,MFC为其创建一个消息映射表,当有消息传入时,经过查找消息映射表就能够找到对应的函数来处理。
消息映射表其实就是类的一个消息处理成员,若是它的基类也同消息有关,则这个成员的某个子段指向基类的,???
2.6.1 DECLARE_MESSAGE_MAP
这是一个宏,于消息有关的类都有这个宏,下面是该宏的定义:
#define DECLARE_MESSAGE_MAP() \
protected: \
static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \
virtual const AFX_MSGMAP* GetMessageMap() const; \
例如在CView类中展开该宏,效果以下:
class CView : public CWnd
{
… …
//DECLARE_MESSAGE_MAP()
static const AFX_MSGMAP* PASCAL GetThisMessageMap();
virtual const AFX_MSGMAP* GetMessageMap() const;
}
能够看出,该宏实际上为CView类添加了两个函数,一个是静态函数,另外一个是虚函数。
2.6.2 BEGIN…ON…END宏
在定义类的成员时,也要对DECLARE_MESSAGE_MAP()添加的函数进行实现,这两个宏就起这个做用,下面是这两个宏的定义:
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
const AFX_MSGMAP* theClass::GetMessageMap() const \
{ return GetThisMessageMap(); } \
const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \
{ \
typedef theClass ThisClass; \
typedef baseClass TheBaseClass; \
static const AFX_MSGMAP_ENTRY _messageEntries[] = \
{
#define END_MESSAGE_MAP() \
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
}; \
static const AFX_MSGMAP messageMap = \
{ &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \
return &messageMap; \
}
能够看出,第一个参数theClass就是当前类的名称,第二个参数baseClass多是基类的名称,但不必定(例如CWinApp)。
一样仍是以CView为例,看一下这两个宏的使用:
BEGIN_MESSAGE_MAP(CView, CWnd)
ON_WM_PAINT()
… …
// Standard commands for split pane
ON_COMMAND_EX(ID_WINDOW_SPLIT, &CView::OnSplitCmd)
… …
END_MESSAGE_MAP()
省略号部分表明还有其余内容,为了简单这里仅写3个。
展开后的结果以下:
const AFX_MSGMAP* CView::GetMessageMap() const
{
return GetThisMessageMap();
}
const AFX_MSGMAP* PASCAL CView::GetThisMessageMap()
{
typedef CView ThisClass;
typedef CWnd TheBaseClass;
static const AFX_MSGMAP_ENTRY _messageEntries[] =
{
{
WM_PAINT,
0,
0,
0,
AfxSig_vv,
(AFX_PMSG)(…)( &CView::OnPaint))
}, //ON_WM_PAINT
… …
{
WM_COMMAND,
CN_COMMAND,
(WORD)ID_WINDOW_SPLIT,
(WORD)ID_WINDOW_SPLIT,
AfxSigCmd_EX,
(AFX_PMSG)(…)(&CView::OnSplitCmd))
},// ON_COMMAND_EX
… …
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } //END_MESSAGE_MAP()
};
static const AFX_MSGMAP messageMap =
{
&TheBaseClass::GetThisMessageMap, &_messageEntries[0]
};
return &messageMap;
}
看一下函数:GetThisMessageMap
它在内部建立一个局部静态数据结构数组_messageEntries,这说明调用过该函数该数组才会被初始化。而这两个宏之间的部分就是用来初始化该结构数组变量的数据。
因此,从GetMessageMap()能够看出,它返回的实际是一个局部静态结构数组的指针。
下面详细分析一下该函数GetThisMessageMap。
2.6.3 GetThisMessageMap
_messageEntries是AFX_MSGMAP_ENTRY结构数组,它实际就是消息映射表,记录每个消息ID(第一个参数)到消息处理函数指针(最后一个参数,)的映射,固然还有其它一些信息,例如消息来自于哪一个控件(该控件ID)等等,这个消息结构的其它成员之后再学习。
实际上,_messageEntries记录的都是大部分都是当前类可以处理的消息映射表,宏BEGIN_MESSAGE_MAP(theClass, baseClass)的第一个参数正是用在这个地方,指出可以处理消息的函数所属的类。
messageMap是一个结构变量,它的成员颇有意思,第一个成员是一个函数指针,它指向基类(也多是其它类)的GetThisMessageMap函数指针;上文提到的宏BEGIN_MESSAGE_MAP(theClass, baseClass)的第二个参数正是用在这个地方。
另外一个成员指向_messageEntries,也就是消息映射表。
很显然,经过该函数能够很容易找到相应的消息处理函数,即便当前类不能处理某,还能够在基类中(或指定地类中)寻找。
下面举几个消息映射的例子。
2.6.4 CCmdTarget的消息映射
看一下这个类消息映射表的定义:
const AFX_MSGMAP* CCmdTarget::GetMessageMap() const
{
return GetThisMessageMap();
}
const AFX_MSGMAP* CCmdTarget::GetThisMessageMap()
{
static const AFX_MSGMAP_ENTRY _messageEntries[] =
{
{ 0, 0, AfxSig_end, 0 } // nothing here
};
static const AFX_MSGMAP messageMap =
{
NULL,
&_messageEntries[0]
};
return &messageMap;
}
该类没有BEGAIN…ON…END宏,看一下消息映射表_messageEntries,它只有一个元素{ 0, 0, AfxSig_end, 0 },显然,该元素不会处理任何消息。
再看看messageMap,第一个成员为NULL,也就是说,若是一个消息达这里尚未被处理,那么这个消息不再会被处理了。
2.6.5 CView的消息映射
先看一下CView类(这里仅写出部分代码):
class AFX_NOVTABLE CView : public CWnd
{
...
afx_msg void OnPaint();
afx_msg BOOL OnSplitCmd(UINT nID);
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CView, CWnd)
ON_WM_PAINT()
ON_COMMAND_EX(ID_WINDOW_SPLIT, &CView::OnSplitCmd)
END_MESSAGE_MAP()
展开这部分代码后以下:
class AFX_NOVTABLE CView : public CWnd
{
...
afx_msg void OnPaint();
afx_msg BOOL OnSplitCmd(UINT nID);
static const AFX_MSGMAP* PASCAL GetThisMessageMap();
virtual const AFX_MSGMAP* GetMessageMap() const;
};
const AFX_MSGMAP* CCmdTarget::GetMessageMap() const
{
return GetThisMessageMap();
}
const AFX_MSGMAP* CCmdTarget::GetThisMessageMap()
{
static const AFX_MSGMAP_ENTRY _messageEntries[] =
{
{ WM_PAINT, 0, 0, 0, AfxSig_vv, (…)( &ThisClass :: OnPaint)) },//ON_WM_PAINT
{ WM_COMMAND, CN_COMMAND, (WORD) ID_WINDOW_SPLIT,
(WORD) ID_WINDOW_SPLIT, AfxSigCmd_EX,(...)&CView::OnSplitCmd)) },
{ 0, 0, AfxSig_end, 0 } // nothing here
};
static const AFX_MSGMAP messageMap =
{
&CWnd::GetThisMessageMap, &_messageEntries[0]
};
return &messageMap;
}
void CView::OnPaint(){…}
BOOL CView:: OnSplitCmd(UINT nID);{…}
…表示一种强制类型转换,这里暂不考虑它。
看一下粗体部分字体,OnPaint和OnSplitCmd是必须定义的。
这也说明,消息WM_PAINT映射倒CView::OnPaint函数;再看一下另外一个函数OnSplitCmd,它能够处理消息WM_COMMAND,但它还有两个参数,详情能够参看msdn,注意宏ON_COMMAND_EX展开后的代码,这说明若是有一个WM_COMMAND消息,且它的第一个参数是CN_COMMAND,它第二个参数是ID_WINDOW_SPLIT时,才能被OnSplitCmd处理。
因此,以上说明不一样的消息,它们的宏定义多少也有区别,本质上,也就是对消息映射表_messageEntries的各个元素初始化也是不一样的。因此消息映射表对应的结构体AFX_MSGMAP_ENTRY各个成员正式反映了消息的不一样类型,有些消息须要使用较多的成员,但有些消息使用不多的成员。
CWnd是CView的基类,它也是BEGIN_MESSAGE_MAP的第二个参数。
2.6.6 消息的分派
当窗口产生一个消息后,它从哪里开始进入消息映射表中呢?
MFC提供了一个全局函数AfxRegisterWndClass,暂时不关心该函数是如何调用的,但能够确定一点,当有窗口建立时,会调用该函数,看一下该函数体的部分代码:
LPCTSTR AFXAPI AfxRegisterWndClass(…)
{
WNDCLASS wndcls;
… …
wndcls.lpfnWndProc = DefWindowProc;
… …
}
显然,这个函数的做用是注册一个窗口类WNDCLASS (若是窗口类不存在),其中,成员lpfnWndProc是一个函数指针,它指向一个消息处理函数,在窗口消息循环中(CWinApp::Run函数)中调用(多是间接)DispatchMessage函数,该函数的做用是将消息传给lpfnWndProc所指向的函数,这就构成了基本的窗口处理过程。
然而,MFC还不只这么简单,看一下面几个函数(部分代码):
BOOL CWnd::CreateEx(...)
{
… …
AfxHookWindowCreate(this);
… …
}
void AFXAPI AfxHookWindowCreate(CWnd* pWnd)
{
…
pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,
_AfxCbtFilterHook, NULL, ::GetCurrentThreadId());
…
}
CWnd建立窗口时安装了一个消息挂钩,这个挂钩的类型是WH_CBT,关于这种类型挂钩详细类型参看msd,绝大部分的windows消息(事实上我没有遇到过有哪一个消息例外)都能被该挂钩函数过滤,并且,在windows经过正常方法处理消息以前,都会先调用挂钩函数。
下面再看一下挂钩函数_AfxCbtFilterHook:
_AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)
{
… …
if (code != HCBT_CREATEWND)
{
// wait for HCBT_CREATEWND just pass others on...
return CallNextHookEx(pThreadState->m_hHookOldCbtFilter, code,
wParam, lParam);
}
… …
WNDPROC afxWndProc = AfxGetAfxWndProc();
oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC,
(DWORD_PTR)afxWndProc);
… …
}
WNDPROC AFXAPI AfxGetAfxWndProc()
{
… …
return &AfxWndProc;
}
这里,又将消息处理函数换成AfxWndProc。至此,能够清楚的知道,::DispatchMessage最终会将窗口消息传给AfxWndProc。
2.6.7 消息的传递
先看看消息的起头函数:
LRESULT CALLBACK AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
// all other messages route through message map
CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
… …
if (pWnd == NULL || pWnd->m_hWnd != hWnd)
return ::DefWindowProc(hWnd, nMsg, wParam, lParam);
return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);
}
LRESULT AFXAPI AfxCallWndProc(…)
{
…
lResult = pWnd->WindowProc(nMsg, wParam, lParam);
…
return lResult;
}
在MFC中,不少窗口类都有成员函数WindowProc(包括继承的),大部分都是从CWnd派生来的,看一下下面几个函数:
LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// OnWndMsg does most of the work, except for DefWindowProc call
LRESULT lResult = 0;
if (!OnWndMsg(message, wParam, lParam, &lResult))
lResult = DefWindowProc(message, wParam, lParam);
return lResult;
}
LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
{
if (m_pfnSuper != NULL)
return ::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam);
WNDPROC pfnWndProc;
if ((pfnWndProc = *GetSuperWndProcAddr()) == NULL)
return ::DefWindowProc(m_hWnd, nMsg, wParam, lParam);
else
return ::CallWindowProc(pfnWndProc, m_hWnd, nMsg, wParam, lParam);
}
若是OnWndMsg执行失败,调用DefWindowProc,OnWndMsg函数作了哪些事情呢?
BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
…
if (message == WM_COMMAND){…(OnCommand(wParam, lParam);…}
if (message == WM_NOTIFY) {…(OnNotify(wParam, lParam);…}
if (message == WM_ACTIVATE) _AfxHandleActivate(…);
if (message == WM_SETCURSOR) _AfxHandleSetCursor(…);
… …
}
从OnWndMsg能够看出,对于不一样类型的消息,调用的处理函数也有不一样。
… …
for (/* pMessageMap already init'ed */; pMessageMap->pfnGetBaseMap != NULL;
pMessageMap = (*pMessageMap->pfnGetBaseMap)())
{
if (message < 0xC000)
{
… …
AfxFindMessageEntry(pMessageMap->lpEntries, message, 0, 0)
… …
}
else
{
lpEntry = pMessageMap->lpEntries;
while(lpEntry = AfxFindMessageEntry(lpEntry, 0xC000, 0, 0)) != NULL)
{
… …
}
}
MFC序列化
1.序列化概念:采用数据流的方式,将数据依次写入或者读取文件。是二进制的存储方式。
2.序列化相关类:CFile(文件类);CArchive(封装了序列化操做,完成数据读写的具体操做);CObject中与序列化相关的两个函数,CObject::Serialize()数据读写的虚函数;CObject::IsSerializable()判断是否支持序列化。
3.序列化的使用:
1>建立或者打开文件 CFile::Open()
2>定义CArchive对象
3>使用CArchive对象进行写(存储”<<”)或者读(加载”>>”);同一个文件的数据存储和加载的顺序和类型必须一致
4>关闭CArchive对象 CArchive::Close()
5>关闭文件 CFile::Close()
4.对象的序列化和反序列化
对象的序列化——将对象的类的信息以及类中的数据保存到文件中的过程。
对象的反序列化——根据读取的类的信息,建立对象,并将对象的数据读入的过程。
使用过程:
(1).定义支持序列化的类
1>继承自CObject
2>重写CObject类的Serialize()函数
3>在类的定义中添加宏 DECLARE_SERIAL
4>在类的实现中添加宏 IMPLEMENT_SERIAL
(2).建立或者打开文件 CFile::Open()
(3).定义CArchive对象
(4).将类的对象保存到CArchive对象中,或者从CArchive对象读取
(5).关闭CArchive对象 CArchive::Close()
(6).关闭文件 CFile::Close()
5.对象的序列化带来的好处
1> 以某种存储形式使自定义对象持久化;
2> 将对象从一个地方传递到另外一个地方。
3> 使程序更具维护性。
实例
序列化实例
[cpp]
//新建MFC的MFCSerialize工程,删除掉自动生成的文件,并设置 属性->连接器->系统->子系统 控制台 (/SUBSYSTEM:CONSOLE)
// MFCSerialize.cpp : 定义应用程序的类行为。
#include "stdafx.h"
CWinApp the;
using namespace std;
void Store()//写操做,存储
{
CFile file;
if (!file.Open(L"c:\\serial.data",CFile::modeCreate|CFile::modeWrite))
{
return;
}
CArchive ar(&file,CArchive::store);//定义CArchive对象
try
{
//执行写操做
ar<<100<<12.25;
CString strData=L"Hello CArchive";
ar<<strData;
}
catch (CException* e)
{
}
ar.Close();
file.Close();
}
void Load()//读操做,加载
{
CFile file;
if (!file.Open(L"c:\\serial.data",CFile::modeRead))
{
return;
}
CArchive ar(&file,CArchive::load);
int num=0;
double fNum=0.0f;
CString strData=L"";
ar>>num>>fNum>>strData;
ar.Close();
file.Close();
wprintf(L"nNum=%d; fNum=%.2f; %s\n",num,fNum,strData);
}
void main()
{
Store();
Load();
}
//新建MFC的MFCSerialize工程,删除掉自动生成的文件,并设置 属性->连接器->系统->子系统 控制台 (/SUBSYSTEM:CONSOLE)
// MFCSerialize.cpp : 定义应用程序的类行为。
#include "stdafx.h"
CWinApp the;
using namespace std;
void Store()//写操做,存储
{
CFile file;
if (!file.Open(L"c:\\serial.data",CFile::modeCreate|CFile::modeWrite))
{
return;
}
CArchive ar(&file,CArchive::store);//定义CArchive对象
try
{
//执行写操做
ar<<100<<12.25;
CString strData=L"Hello CArchive";
ar<<strData;
}
catch (CException* e)
{
}
ar.Close();
file.Close();
}
void Load()//读操做,加载
{
CFile file;
if (!file.Open(L"c:\\serial.data",CFile::modeRead))
{
return;
}
CArchive ar(&file,CArchive::load);
int num=0;
double fNum=0.0f;
CString strData=L"";
ar>>num>>fNum>>strData;
ar.Close();
file.Close();
wprintf(L"nNum=%d; fNum=%.2f; %s\n",num,fNum,strData);
}
void main()
{
Store();
Load();
}运行结果:
\
对象序列化实例
[cpp]
//新建MFC的MFCObject工程,删除掉自动生成的文件,并设置 属性->连接器->系统->子系统 控制台 (/SUBSYSTEM:CONSOLE)
// MFCObject.cpp : 定义应用程序的类行为。
#include "stdafx.h"
CWinApp theApp;
using namespace std;
//定义支持序列化得类
class CStudent:public CObject
{
public:
CStudent(){}
CStudent(CString name,int age);
void Show();
virtual void Serialize(CArchive& ar);
_DECLARE_DYNCREATE(CStudent) //动态建立宏
AFX_API friend CArchive& AFXAPI operator>>(CArchive& ar, CStudent*& pOb);
private:
CString m_strName;
int m_nAge;
};
CStudent::CStudent(CString name,int age)
{
m_strName=name;
m_nAge=age;
}
void CStudent::Show()
{
printf("学员信息:%S,%d\n",m_strName,m_nAge);//大写'S'在ansi中,表明后面是unic字符串
}
void CStudent::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
ar<<m_strName<<m_nAge;
}
else
{
ar>>m_strName>>m_nAge;
}
}
CArchive& AFXAPI operator>>(CArchive& ar, CStudent*& pOb)
{
pOb=(CStudent*)ar.ReadObject(RUNTIME_CLASS(CStudent));
return ar;
}
CObject* PASCAL CStudent::CreateObject()
{
return new CStudent;
}
_IMPLEMENT_RUNTIMECLASS(CStudent,CObject,1,CStudent::CreateObject);
AFX_CLASSINIT _init_CStudent(RUNTIME_CLASS(CStudent));
void ObjStore(CStudent&stu)
{
CFile file;
file.Open(L"c:\\student.data",CFile::modeCreate|CFile::modeWrite);
CArchive ar(&file,CArchive::store);
ar<<&stu;
ar.Close();
file.Close();
}
void ObjLoad()
{
CFile file;
file.Open(L"c:\\student.data",CFile::modeRead);
CArchive ar(&file,CArchive::load);
CStudent *pStu=NULL;
ar>>pStu;
ar.Close();
if (pStu)
{
pStu->Show();
}
}
void main()
{
CStudent stu(L"ZhangShan",18);
ObjStore(stu);//对象的序列化
ObjLoad();//对象的反序列化
}
/*
class CStudent
{
...
_DECLARE_DYNCREATE(CStudent)//动态建立宏
AFX_API friend CArchive& AFXAPI operator>>(CArchive& ar,CStudent* &pOb);
}
1.operator>>-建立类的对象并读取数据
2._init_CStudent-变量展开后是一个函数AfxClassInit,将CStudent的运行时类信息保存到程序的模块中m_classList
3.序列化的过程
1> 获取对象的运行时类信息: CRuntimeClass* pClassRef = pOb->GetRuntimeClass();
2> 将类的版本和名称写入到Archive对象中WriteClass(pClassRef);调用 pClassRef->Store(*this);,在函数中,将类的版本、大小、名称保存到Archive对象
3> 调用对象的Serialize函数((CObject*)pOb)->Serialize(*this);
4> 在Serialize函数中,将对象的数据保存到Archive对象中 ar<<m_strName<<m_nAge;
4.反序列化的过程
1> _init_CStudent变量将类的运行时类信息保存到应用程序中。
2> 从文件中读取类的名称和版本ReadClass函数,调用pClassRef = CRuntimeClass::Load(*this, &nSchema)在函数中,从Archive对象读取类的版本、大小 和名称。
3> 使用从文件中读取的类的名称,在程序的链表m_classList中,去查找类的运行时信息
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
for (pClass = pModuleState->m_classList; pClass != NULL;pClass = pClass->m_pNextClass)
{
if (lstrcmpA(szClassName, pClass->m_lpszClassName) == 0)
{
AfxUnlockGlobals(CRIT_RUNTIMECLASSLIST);
return pClass;
}
}
4> ReadClass函数执行完毕后,调用CreateObject函数 pOb = pClassRef->CreateObject();调用宏展开后的函数,建立CStudent类的对象
5> 调用对象的Serialize函数 pOb->Serialize(*this);
6> 在Serialize函数中,读取对象的数据 ar>>m_strName>>m_nAge;
*/
//新建MFC的MFCObject工程,删除掉自动生成的文件,并设置 属性->连接器->系统->子系统 控制台 (/SUBSYSTEM:CONSOLE)
// MFCObject.cpp : 定义应用程序的类行为。
#include "stdafx.h"
CWinApp theApp;
using namespace std;
//定义支持序列化得类
class CStudent:public CObject
{
public:
CStudent(){}
CStudent(CString name,int age);
void Show();
virtual void Serialize(CArchive& ar);
_DECLARE_DYNCREATE(CStudent) //动态建立宏
AFX_API friend CArchive& AFXAPI operator>>(CArchive& ar, CStudent*& pOb);
private:
CString m_strName;
int m_nAge;
};
CStudent::CStudent(CString name,int age)
{
m_strName=name;
m_nAge=age;
}
void CStudent::Show()
{
printf("学员信息:%S,%d\n",m_strName,m_nAge);//大写'S'在ansi中,表明后面是unic字符串
}
void CStudent::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
ar<<m_strName<<m_nAge;
}
else
{
ar>>m_strName>>m_nAge;
}
}
CArchive& AFXAPI operator>>(CArchive& ar, CStudent*& pOb)
{
pOb=(CStudent*)ar.ReadObject(RUNTIME_CLASS(CStudent));
return ar;
}
CObject* PASCAL CStudent::CreateObject()
{
return new CStudent;
}
_IMPLEMENT_RUNTIMECLASS(CStudent,CObject,1,CStudent::CreateObject);
AFX_CLASSINIT _init_CStudent(RUNTIME_CLASS(CStudent));
void ObjStore(CStudent&stu)
{
CFile file;
file.Open(L"c:\\student.data",CFile::modeCreate|CFile::modeWrite);
CArchive ar(&file,CArchive::store);
ar<<&stu;
ar.Close();
file.Close();
}
void ObjLoad()
{
CFile file;
file.Open(L"c:\\student.data",CFile::modeRead);
CArchive ar(&file,CArchive::load);
CStudent *pStu=NULL;
ar>>pStu;
ar.Close();
if (pStu)
{
pStu->Show();
}
}
void main()
{
CStudent stu(L"ZhangShan",18);
ObjStore(stu);//对象的序列化
ObjLoad();//对象的反序列化
}
/*
class CStudent
{
...
_DECLARE_DYNCREATE(CStudent)//动态建立宏
AFX_API friend CArchive& AFXAPI operator>>(CArchive& ar,CStudent* &pOb);
}
1.operator>>-建立类的对象并读取数据
2._init_CStudent-变量展开后是一个函数AfxClassInit,将CStudent的运行时类信息保存到程序的模块中m_classList
3.序列化的过程
1> 获取对象的运行时类信息: CRuntimeClass* pClassRef = pOb->GetRuntimeClass();
2> 将类的版本和名称写入到Archive对象中WriteClass(pClassRef);调用 pClassRef->Store(*this);,在函数中,将类的版本、大小、名称保存到Archive对象
3> 调用对象的Serialize函数((CObject*)pOb)->Serialize(*this);
4> 在Serialize函数中,将对象的数据保存到Archive对象中 ar<<m_strName<<m_nAge;
4.反序列化的过程
1> _init_CStudent变量将类的运行时类信息保存到应用程序中。
2> 从文件中读取类的名称和版本ReadClass函数,调用pClassRef = CRuntimeClass::Load(*this, &nSchema)在函数中,从Archive对象读取类的版本、大小 和名称。
3> 使用从文件中读取的类的名称,在程序的链表m_classList中,去查找类的运行时信息
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
for (pClass = pModuleState->m_classList; pClass != NULL;pClass = pClass->m_pNextClass)
{
if (lstrcmpA(szClassName, pClass->m_lpszClassName) == 0)
{
AfxUnlockGlobals(CRIT_RUNTIMECLASSLIST);
return pClass;
}
}
4> ReadClass函数执行完毕后,调用CreateObject函数 pOb = pClassRef->CreateObject();调用宏展开后的函数,建立CStudent类的对象
5> 调用对象的Serialize函数 pOb->Serialize(*this);
6> 在Serialize函数中,读取对象的数据 ar>>m_strName>>m_nAge;
*/运行结果:
图略
MFC学习一箩筐