1.基本知识
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//对应的是应用程序
{
HINSTANCE m_hInstance;
HINSTANCE m_hPrevInstance;
LPSTR m_lpCmdLine;
Int m_nCmdShow;
…
Public:
Virtual BOOL InitApplication();
Virtual BOOL InitInstance();
Virtual int Run();
…
};
几乎可以说CWinAPP取代了WinMain在SDK中的地位.这并不是说MFC程序没有WinMain,而是说传统上SDK程序的WinMain所完成的工作现在由CWinApp的三个函数完成:
Virtual BOOL InitApplication();
Virtual BOOL InitInstance();
Virtual int Run();
WinMain只是扮演驾驭它们的角色.
CWinApp应该有个成员变量记录主窗口的handle.在MFC2.5中的确有m_pMainWnd这个变量,从MFC4.x开始,m_pMainWnd已经被移往CWinThread中了(它是CWinApp的父类):
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()//秘密在这里:将消息和相应的处理函数进//行关联
};
2.程序启动
(1)windows操作系统为应用程序创建进程核心对象,并为该应用程序分配4GB的进程地址空间,系统加载器将应用程序可执行文件映像以及一些必要的代码(包括数据和一些应用程序使用的dlls)加载到应用程序的进程地址空间中。
(2)windows 操作系统在初始化该应用程序进程的同时,将自动为该应用程序创建一个主线程,该主线程与c/c++运行时库的启动函数一道开始运行。当应用程序编译后开始链接时,系统的链接器会根据你的应用程序的设置为你的应用程序选择一个c/c++运行时库的启动函数(注释:这些函数声明在..\visual studio.net\vc7\crt\src\crt0.c中)
一般的ansi版本的gui的应用程序的c/c++运行时库的启动函数为:
int winmaincrtstartup(void);
其它版本的c/c++运行时库的启动函数如下:
ansi版本的cui的应用程序:
int maincrtstartup(void);
unicode版本的cui的应用程序:
int wmaincrtstartup(void);
unicode版本的gui的应用程序:
int wwinmaincrtstartup(void);
c/c++运行时库的启动函数的主要功能为初始化c/c++运行时库和为所有全局和静态的c++类对象调用构造函数。
前面所说的c/c++运行时库的启动函数的主要功能之一是为所有全局和静态的c++类对象调用构造函数。侯捷老师所说的"引爆器"---
cmywinapp theapp这个application
object就是由启动函数调用其构造函数构造出来的。3.引爆器—Application Object
Application Object,每个MFC应用程序都有一个,而且也只有一个.当执行应用程序时,这个全局对象产生,于是构造函数执行起来.父类CWinApp的构造函数内容如下:
CWinApp:: CWinApp(LPCTSTR lpszAppName)
{
m_pszAppName = lpszAppName;
AFX_MODULE_THREAD_STATE * pThreadState = AfxGetMoudleThreadState();
pThreadState->m_pCurrentWinThread = this;
m_hThread = ::GetCurrentThread();
m_nThreadID = ::GetCurrentThreadId();
AFX_MOUDLE_STATE * pModueState = AfxGetModuleState();
pModuleState->m_pCurrentWinApp = this;
m_hInstance = NULL;
m_pszHelpFilePath = NULL;
m_pszProfileName = NULL;
m_pszRegistryKey = NULL;
m_pszExeName = NULL;
m_lpCmdLine = NULL;
m_pCmdInfo = NULL;
…
}
从源代码中可以看出cwinapp的构造函数主要收集了一些关于应用程序主线程的信息及初始化一些相关应用程序的信息。值得注意的是cwinapp类的一
些主要的数据成员如:m_hinstance,m_lpcmdline,m_pcmdinfo及m_atomapp等都初始化为null,这些成员在后面
将被重新赋值。 4.隐晦不明的WinMain
c/c++运行时库的启动函数int winmaincrtstartup(void);所调用的winmain函数---同时也是主线程的入口函数:
int winapi _twinmain(hinstance hinstance, hinstance hprevinstance,lptstr lpcmdline,int ncmdshow)
{
Return AfxWinMain(hInstance, hPrrevInstance, lpCmdLine, nCmdShow);
}
也就是说:MFC中的winmain函数其实什么也没做,只是调用了一个函数afxwinmain。
对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对象指针
5.上述四大模块的功能
5.1 afx的内部初始化
afxwininit函数是既cwinapp类构造函数后的又一个重量级的函数。
注释:该函数定义在..\visual studio.net\vc7\atlmfc\src\mfc\appinit.cpp中。
前面说过:"cwinapp类的一些主要的数据成员在后面将被重新赋值。",afxwininit函数就是这些数据成员被赋值的地方,它重新初始化这些在整
个程中都扮演重要角色的成员,并且调用afxinitthread()为主线程作了一些初始化工作,这些都为以后mfc框架的正常运作铺平了道路。
5.2应用程序的全局初始化
Initapplication()函数(virtual)为程序进行全局初始化
在mfc文档中有这么一句话"the cwinapp::Initapplication member function is obsolete in mfc.",所以你大多情况下不用在意这个virtual函数。
5.3 应用程序的标准实例化
首先来看MSDN中的一段话中描述的关于Initinstance()函数的作用:
Windows allows several copies of the same program to run at the same
time.
Application initialization is conceptually divided into two sections:
one-time application initialization that is done the first time the program
runs, and instance initialization that runs each time a copy of the program
runs, including the first time. The framework's implementation of WinMain
calls this function.
Override InitInstance to initialize each new instance of your
application running under Windows. Typically, you override InitInstance
to construct your main window object and set the CWinThread::m_pMainWnd
data member to point to that window.
BOOL CMyApp::InitInstance()
{
LoadStdProfileSettings(); // Load standard INI file options
// Register the application's document templates. Document templates serve as the connection between documents, frame windows and views.
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(IDR_MAINFRAME,
RUNTIME_CLASS(CMyDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CMyView));
AddDocTemplate(pDocTemplate);
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// Dispatch commands specified on the command line. Will return FALSE if app was launched with /RegServer, /Register, /Unregserver or /Unregister.
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// The one and only window has been initialized, so show and update it//注意到这里主窗口已经初始化完毕了!真是神不知鬼不觉的
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}
关于这一部分我将在下一篇文章《MFC文档视图结构内幕》中详细介绍。
5.4 消息泵的启动
众所周知,windows是一个以消息为基础,以事件驱动的操作系统,每一个win32程序也都是如此。那么mfc应用程序是如何实现消息机制的呢?mfc应用程序框架将这种消息机制包装到了一个"消息泵"中,而这个"消息泵"在cmywinapp::Initinstance()中被启动了。其源代码如下:
注释1:该函数定义在..\visual studio.net\vc7\atlmfc\src\mfc\appcore.cpp中。
// main running routine until application exits
int cwinapp::run()
{
if (m_pmainwnd == null && afxolegetuserctrl())
{
afxpostquitmessage(0);
}
return cwinthread::run();
}
由上面的源代码看出:cwinapp::run()调用了其基类的run()函数,继续看源代码:
int cwinthread::run()
{
assert_valid(this);
_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));
}
}
//cwinthread implementation helpers
bool cwinthread::pumpmessage()
{
return afxinternalpumpmessage();
}
bool afxapi afxinternalpumpmessage()//部分源码
{
_afx_thread_state *pstate = afxgetthreadstate();
if (!::getmessage(&(pstate->m_msgcur), null, null, null))
{
...//
return false;
}
// process this message
if (pstate->m_msgcur.message != wm_kickidle
&&
!afxpretranslatemessage(&(pstate->m_msgcur)))
{
::translatemessage(&(pstate->m_msgcur));
::dispatchmessage(&(pstate->m_msgcur));
}
return true;
}
终于出现了::translatemessage和::dispatchmessage
经过这么大篇幅的挖掘,一个mfc应用程序从生到死的流程我们都已目睹完了!
阅读(1229) | 评论(0) | 转发(0) |