MFC程序的来龙去脉(causal relations)
MFC的程序如何运行,第一件事情就是找出MFC程序的进入点.MFC程序也是Windows程序.所以它应该也有一个WinMain.但在程序中好象看不到..
在程序进入点之前,还有一个(而且仅有一个)全局对象,这就是所谓的application object,当操作系统将程序加载并激活时,这个全局对象获得配置,其构造函数会先执行,比WinMain更早.所以先看application object.
CWinAPP和CFrameWnd
Windows程序中WinMain和WndProc这两个部分其实都有相当程度的不变性.MFC就把有着相当固定行为的WinMain内部操作封装在CWinApp中,把着相当固定行为的WinProc内部操作封装在CFrameWnd中.即
#CWinApp代表程序本体
#CFrameWnd代表一个主框窗口(Frame Window)
WinMain内部操作和WinProc内部操作都有着相当程度的固定行为,但它们毕竟需要面对不同应用程序而有某中变化.所以,我们必须以这两个类为基础,派生自己的类,并改写其中一部分成员函数.
Class CMyWinApp : public CWinApp
{
…
};
Class CMyFrameWnd : public CFrameWnd
{
…
};
CWinApp—取代WinMain的地位
CWinApp的派生对象被称为application object, CWinApp本身就代表一个程序本体.一个程序的本体是与程序本身有关而不于窗口有关的数据或动作无关.比如系统传进来的四个WinMain参数,InitApplication和InitInstance,消息循环等等.
下面是CWinApp的声明(MFC 4.x):
Class CWinApp : public CWinThread
{
//Attributes
//Startup arg( do not change )
HINSTANCE m_hInstance;
HINSTANCE m_hPrevInstance;
LPSTR m_lpCmdLine;
Int m_nCmdShow;
//Running args (can be changed in InitInstance)
LPCTSTR m_pszAppName; //human readable name
LPCTSTR m_pszRegisteryKey; //used for registry entries
Public: //set in constructor to override default
LPCTSTR m_pszExecName; //executable name (no spaces)
LPCTSTR m_pszHelpFilePath; //default based on module path
LPCTSTR m_pszProfileName; // default based on app path
Public:
//hooks for youe initialization code
Virtual BOOL InitApplication();
//overrides for implementation
Virtual BOOL InitInstance();
Virtual int ExitInstance();
Virtual int Run();
Virtual BOOL OnIdle(LONG lCount);
…
};
几乎可以说CWinAPP取代了WinMain在SDK中的地位.这并不是说MFC程序没有WinMain,而是说传统上SDK程序的WinMain所完成的工作现在由CWinApp的三个函数完成:
Virtual BOOL InitApplication();
Virtual BOOL InitInstance();
Virtual int Run();
WinMain只是扮演驾驭它们的角色.
CWinApp应该有个成员变量记录主窗口的handle.在MFC2.5中的确有m_pMainWnd这个变量:
Class CWinApp : public CWinThread
{
//Attributes
//Startup arg( do not change )
HINSTANCE m_hInstance;
HINSTANCE m_hPrevInstance;
LPSTR m_lpCmdLine;
Int m_nCmdShow;
//Running args (can be changed in InitInstance)
CWnd * m_pMainWnd; //main window(optional)
CWnd * m_pActiveWnd; //active main window(may not be m_pMainWnd)
LPCTSTR m_pszAppName; //human readable name
LPCTSTR m_pszRegisteryKey; //used for registry entries
Public: //set in constructor to override default
LPCTSTR m_pszExecName; //executable name (no spaces)
LPCTSTR m_pszHelpFilePath; //default based on module path
LPCTSTR m_pszProfileName; // default based on app path
Public:
//hooks for youe initialization code
Virtual BOOL InitApplication();
//overrides for implementation
Virtual BOOL InitInstance();
Virtual int ExitInstance();
Virtual int Run();
Virtual BOOL OnIdle(LONG lCount);
…
};
从MFC4.x开始,m_pMainWnd已经被移往CWinThread中了(它是CWnd的父类):
Class CFramwWnd : public CCmdTarget
{
//Attributes
CWnd * m_pMainWnd; //main window (usually same AfxGetApp()->m_pMainWnd)
CWnd * m_pActiveWnd; //active main window (may not be m_pMainWnd)
//Only valid while running
HANDLE m_hThread ; //this thread’s HANDLE
DWORD m_nThreadID; //this thread’s ID
Int GetThreadPriority();
BOOL SetThreadPriority(int nPriority);
//Operations
DWORD SuspendThread();
DWORD ResumeThread();
//Overridables
//thread initialization
Virtual BOOL InitInstance();
//running and idle processing;
Virtual int Run();
Virtual BOOL PreTranslateMessage(MSG *pMsg);
Virtual BOOL PumpMessage(); //low level message pump
Virtual BOOL OnIdle(LONG lCount); //return TRUE if more idle processing
Public:
//valid after construction
AFX)THREADPROC m_pfnThreadProc;
…
};
CFrameWnd—取代WndProc的地位
CFrameWnd主要用来掌握一个窗口,几乎说它是用来取代SDK程序中的窗口函数的地位.传统SDK的窗口函数写法是:
Long FAR PASCAL WndProc(HWND hWnd,UINT msg,WORD wParam,LONG lParam)
{
Switch(msg)
{
Case WM_COMMAND:
Switch(wParam)
{
Case IDM_ABOUT: OnAbout(hWnd,wParam,lParam);
Break;
}
Break:
Case WM_PAINT:
OnPaint(hWnd,wParam,lParam);
Break;
Default:
DefWindowProc(hWnd,msg,wParam,lParam);
}
}
MFC程序有新的做法,声明如下:
Class CMyFrameWnd : public CFrameWnd
{
Public:
CMyFrameWnd();
Afx_msg void OnPaint();
Afx_msg voif OnAbout();
DECLARE_MESSAGE_MAP()
};
引爆器—Application Object
Application Object,每个MFC应用程序都有一个,而且也只有一个.当执行应用程序时,这个全局对象产生,于是构造函数执行起来.父类CWinApp的构造函数内容如下:
CWinApp:: CWinApp(LPCTSTR lpszAppName)
{
m_pszAppName = lpszAppName;
//initialize CWinThread state
AFX_MODULE_THREAD_STATE * pThreadState = AfxGetMoudleThreadState();
pThreadState->m_pCurrentWinThread = this;
m_hThread = ::GetCurrentThread();
m_nThreadID = ::GetCurrentThreadId();
//initialize CWinApp state
AFX_MOUDLE_STATE * pModueState = AfxGetModuleState();
pModuleState->m_pCurrentWinApp = this;
//in non-running state until WinMain
m_hInstance = NULL;
m_pszHelpFilePath = NULL;
m_pszProfileName = NULL;
m_pszRegistryKey = NULL;
m_pszExeName = NULL;
m_lpCmdLine = NULL;
m_pCmdInfo = NULL;
…
}
CWinApp之中的成员变量将因为Application Object这个全局对象的诞生而获得配置和初值.
隐晦不明的WinMain
Application Object配置完成后,再说WinMain.可是我们并没有写WinMain程序代码,这是因为MFC早已准备好并由链接器直接加到应用程序代码中.
注意:”-t”是为了支持Unicode而准备的一个宏.
//in APPMODUL.CPP
Extern ”C” int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrrevInstance,
LPTSTR lpCmdLine , int nCmdShow)
{
//call shared/Exported WinMain
Return AfxWinMain(hInstance, hPrrevInstance, lpCmdLine, nCmdShow);
}
对AfxWinMain稍加整理,它的主要功能如下:
Int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow )
{
ASSERT( hPrevInstance == NULL );
Int nReturnCode = -1;
CwinApp *pApp = AfxGetApp();
AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
pApp->InitApplication();
pApp->InitInstance();
nReturnCode = pApp->Run();
AfxWinTerm();
Return nReturnCode;
}
其中,AfxGetApp是一个全局函数,定义与AFXWINLINL中:
_AFXWIN_INLINE CwinApp * AFXAPI AfxGetApp()
{ return afxCurrentWinApp ;}
而afxCurrentWinApp又定义与AFXWIN.H中:
#define afxCurrentWinApp AfxGetModuleState()->m_pCurrentWinApp
AfxGetApp其实就是取得CmyWinApp对象指针,所以AfxWinMain中这样的操作:
CwimApp *pApp = AfxGetApp();
pApp->InitApplication();
pApp->InitInstance();
nReturnCode = pApp->Run();
其实就相当与调用:
CMyWinAPP:: InitApplication();
CMyWinAPP:: InitInstance();
CMyWinAPP::Run();
因而导致调用:
CWinAPP:: InitApplication();//因为CMyWinAPP并没有改写InitApplication
CWinAPP:: InitInstance();
CWinAPP::Run();
AfxWinInit—AFX内部初始化操作
AfxWinInit是继CwinApp构造函数之后的第一个操作,它的操作摘要如下:
BOOL AFXAPI AfxWinInit( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
ASSERT(hProvInstance==NULL);
//set resource handles
AFX_HANDLE_STATE *pState = AfxGetModuleState();
pState->m_hCurrentInstanceHandle = hInstance;
pState-> m_hCurrentResourceHandle = hInstance;
//fill in the initial state for the application
CwinApp *pApp = AfxGetApp();
If(pApp!=NULL)
{
//Windows specific initialization (not done if no CWinApp)
pApp->m_hInstance = hInstance;
pApp->m_hPrevInstance = hPrevInstance;
pApp->m_lpCmdLine = lpCmdLine;
pApp->m_nCmdShow = nCmdShow;
pApp->SetCurrentHandles();
}
//initialize thread specific data(for main thread)
if(!afxContextIsNull)
AfxInitThread();
Reurn TURE;
}