VC编程之MFC日记选----VC篇
小标 2019-03-20 来源 : 阅读 856 评论 0

摘要:本文主要向大家介绍了VC编程之MFC日记选----VC篇,通过具体的内容向大家展示,希望对大家学习VC编程有所帮助。

本文主要向大家介绍了VC编程之MFC日记选----VC篇,通过具体的内容向大家展示,希望对大家学习VC编程有所帮助。

VC编程之MFC日记选----VC篇

MFC程序的初始化过程
2.1.1 _tWinMain
_tWinMain(…){   
return AfxWinMain(…);   
}   
从代码可以看到,该函数仅调用了AfxMain函数。
_tWinMain是被C/C++运行期函数调用的,这里暂不多讲,因为这部分很复杂。
2.1.2 AfxWinMain
看一下该函数代码,这里仅写主要的调用,将其它都省略掉。
int AFXAPI AfxWinMain(…){  
CWinThread* pThread = AfxGetThread();  
CWinApp* pApp = AfxGetApp();  
AfxWinInit(…);  
pApp->InitApplication();  
if (!pThread->InitInstance()){  
   pThread->m_pMainWnd->DestroyWindow();  
   nReturnCode = pThread->ExitInstance();  
   goto InitFailure;  
}  
nReturnCode = pThread->Run();  

InitFailure:  
#ifdef _DEBUG
// Check for missing AfxLockTempMap calls
if (AfxGetModuleThreadState()->m_nTempMapLock != 0)  
{  
   TRACE(traceAppMsg, 0, "Warning: Temp map lock count non-zero (%ld).\n",  
    AfxGetModuleThreadState()->m_nTempMapLock);  
}  
AfxLockTempMaps();  
AfxUnlockTempMaps(-1);  
#endif
AfxWinTerm();   
}
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;
// 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();
if (!::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL))
{
   ::TranslateMessage(&(pState->m_msgCur));
   ::DispatchMessage(&(pState->m_msgCur));
}
调用了GetMessage(通过其它函数),该函数不但从消息队列取消息,之后还将消息删除。然后分配该消息。
2.1.10 AfxWinMain函数InitFailure后的代码
InitFailure之后的代码现在暂不考虑。
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;    

以上就介绍了VC/MFC的学习,希望对VC/MFC有兴趣的朋友有所帮助。了解更多内容,请关注职坐标编程语言VC/MFC频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程