MFC-原理分析
MFC只留出API供我们调用,我们使用其接口比较简单,但其内部封装及其复杂,要想成为真正的MFC高手,还是得对其内部的封装有所了解,下面就以一个简单的例子,作为对MFC内部结构探讨的开始。
- //HELLO.h
- class CMyApp : public CWinApp
- {
- public:
- virtual BOOL InitInstance ();
- };
- class CMainWindow : public CFrameWnd
- {
- public:
- CMainWindow ();
- protected:
- afx_msg void OnPaint ();
- DECLARE_MESSAGE_MAP ()
- };
- //HELLO.cpp
- #include <afxwin.h>
- #include "Hello.h"
- CMyApp myApp;
- /
- // CMyApp member functions
- BOOL CMyApp::InitInstance ()
- {
- m_pMainWnd = new CMainWindow;
- m_pMainWnd->ShowWindow (m_nCmdShow);
- m_pMainWnd->UpdateWindow ();
- return TRUE;
- }
- /
- // CMainWindow message map and member functions
- BEGIN_MESSAGE_MAP (CMainWindow, CFrameWnd)
- ON_WM_PAINT ()
- END_MESSAGE_MAP ()
- CMainWindow::CMainWindow ()
- {
- Create (NULL, _T ("The Hello Application"));
- }
- void CMainWindow::OnPaint ()
- {
- CPaintDC dc (this);
- CRect rect;
- GetClientRect (&rect);
- dc.DrawText (_T ("Hello, MFC"), -1, &rect,
- DT_SINGLELINE | DT_CENTER | DT_VCENTER);
- }
//HELLO.h class CMyApp : public CWinApp { public: virtual BOOL InitInstance (); }; class CMainWindow : public CFrameWnd { public: CMainWindow (); protected: afx_msg void OnPaint (); DECLARE_MESSAGE_MAP () }; //HELLO.cpp #include <afxwin.h> #include "Hello.h" CMyApp myApp; / // CMyApp member functions BOOL CMyApp::InitInstance () { m_pMainWnd = new CMainWindow; m_pMainWnd->ShowWindow (m_nCmdShow); m_pMainWnd->UpdateWindow (); return TRUE; } / // CMainWindow message map and member functions BEGIN_MESSAGE_MAP (CMainWindow, CFrameWnd) ON_WM_PAINT () END_MESSAGE_MAP () CMainWindow::CMainWindow () { Create (NULL, _T ("The Hello Application")); } void CMainWindow::OnPaint () { CPaintDC dc (this); CRect rect; GetClientRect (&rect); dc.DrawText (_T ("Hello, MFC"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER); }
这个程序及其简单,就是输出"Hello, MFC"。
一、程序的入口
这个程序怎样运行的呢?它即没有main,也没有winmain,以及其他的函数入口,那么它是如何运行,难道会从天而降一个入口函数?
其实这个程序的入口在这里:
_tWinMain->AfxWinMain
-->int AFXAPI AfxWinMain()//位于:WINMAIN.cpp
{
CWinApp* pApp=AfxGetApp();//pApp指向CMyWinApp.
AfxWinInit(...);
pApp->InitApplication();
pApp->InitInstance();
nReturnCode = pApp->Run();
return 0;
}
其中AfxGetApp();//pApp指向CMyWinApp.就是取得CMyWinApp对象,所以AfxWinMain中的
pApp->InitApplication();
pApp->InitInstance();
nReturnCode = pApp->Run();
就相当与:
CMyWinApp->InitApplication();
CMyWinApp->InitInstance();
nReturnCode = CMyWinApp->Run();
也就是:
CWinApp->InitApplication();//CMyWinApp并没有改写InitApplication()
CMyWinApp->InitInstance();//CMyWinApp改写了InitInstance()
nReturnCode = CWinApp->Run();//CMyWinApp并没有改写Run()
所以其入口函数的调用顺序为:
_tWinMain->AfxWinMain -> CWinApp->InitApplication();
CMyWinApp->InitInstance();
CWinApp->Run();
这样便进入到当前程序里。
二、如何产生窗口
- BOOL CMyApp::InitInstance ()
- {
- m_pMainWnd = new CMainWindow;
- m_pMainWnd->ShowWindow (m_nCmdShow);
- m_pMainWnd->UpdateWindow ();
- return TRUE;
- }
BOOL CMyApp::InitInstance () { m_pMainWnd = new CMainWindow; m_pMainWnd->ShowWindow (m_nCmdShow); m_pMainWnd->UpdateWindow (); return TRUE; }
1. m_pMainWnd = new CMainWindow;
其中m_pMainWnd 是CWnd对象指针:CWnd* m_pMainWnd; 在这里用 用户自定义窗口CMainWindow初始化,从而调用CMainWindow::CMainWindow()
- CMainWindow::CMainWindow ()
- {
- Create (NULL, _T ("The Hello Application"));
- }
CMainWindow::CMainWindow () { Create (NULL, _T ("The Hello Application")); }
Create (NULL, _T ("The Hello Application"));用来创建一个窗口。
其声明为:
virtual BOOL Create(
LPCTSTR lpszClassName,
LPCTSTR lpszWindowName,
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
UINT nID,
CCreateContext* pContext = NULL
);
2.m_pMainWnd->ShowWindow (m_nCmdShow);
当Create (NULL, _T ("The Hello Application"));完成后,程序又回到m_pMainWnd->ShowWindow (m_nCmdShow);来显示窗口。
3.m_pMainWnd->UpdateWindow ();
m_pMainWnd->ShowWindow (m_nCmdShow);来显示窗口后调用m_pMainWnd->UpdateWindow ();来更新窗口。
三、消息的传递
窗口创建后如何发送消息,使得响应消息的函数得以调用,本例中响应消息的函数是OnPaint ()用来在窗口中显示"Hello, MFC"字符,那么这个消息如何传递?
在调用m_pMainWnd->UpdateWindow ()时会发送WM_PAINT消息,此消息有谁获得,又是怎样发送出去的?
MFC程序和SDK程序一样,有一个GetMessage/DispatchMessage循环,用来获得消息和发送消息。而且每一个窗口都有一个窗口函数,并以某种方式进行消息的判断和响应。
当m_pMainWnd->UpdateWindow ()发送WM_PAINT消息后,CWinApp->Run();用循环判断来接受消息,并DispatchMessage发送出去。
四、MessageMap消息映射图及其消息响应机制
当CWinApp->Run();把接受到的消息发送出去后,是如何找到相应的响应函数,这就用到了MessageMap消息映射图。
- DECLARE_MESSAGE_MAP ()
- BEGIN_MESSAGE_MAP (CMainWindow, CFrameWnd)
- ON_WM_PAINT ()
- END_MESSAGE_MAP ()
- afx_msg void OnMyPaint ();
DECLARE_MESSAGE_MAP () BEGIN_MESSAGE_MAP (CMainWindow, CFrameWnd) ON_WM_PAINT () END_MESSAGE_MAP () afx_msg void OnMyPaint ();
这些宏构成了复杂的消息映射网。
CWinApp->Run()把接受到的消息发送到CWnd::DefWindowProc中,然后CWnd::DefWindowProc将绕行消息映射表MessageMap,绕行的过程中,发现吻合的函数,于是调用相应的函数。此函数是通过BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间的宏建立的连接。
这个例子中m_pMainWnd->UpdateWindow ()发送WM_PAINT消息,而CWinApp->Run()把接受到的消息发送到CWnd::DefWindowProc中,在映射表中会有
#define ON_WM_PAINT() \
{ WM_PAINT, 0, 0, 0, AfxSig_vv, \
(AFX_PMSG)(AFX_PMSGW) \
(static_cast< void (AFX_MSG_CALL CWnd::*)(void) > ( &ThisClass :: OnPaint)) },
会把WM_PAINT和&ThisClass :: OnPaint联系在一起,那么void CMainWindow::OnPaint ()就会被调用。