4.WinMain详解
首先是AfxWinInit,它隐藏在APPINIT.CPP中,代码如下:
BOOL AFXAPI AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);
// handle critical errors and avoid Windows message boxes
SetErrorMode(SetErrorMode(0) |
SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
// set resource handles
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
pModuleState->m_hCurrentInstanceHandle = hInstance;
pModuleState->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 (!afxContextIsDLL)
AfxInitThread();
return TRUE;
}
其中用到的AfxInitThread函数位于THRDCORE.CPP中,具体代码如下:
void AFXAPI AfxInitThread()
{
if (!afxContextIsDLL)
{
// set message filter proc
_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
ASSERT(pThreadState->m_hHookOldMsgFilter == NULL);
pThreadState->m_hHookOldMsgFilter = ::SetWindowsHookEx
(WH_MSGFILTER,_AfxMsgFilterHook, NULL,
::GetCurrentThreadId());
#ifndef _AFX_NO_CTL3D_SUPPORT
// intialize CTL3D for this thread
_AFX_CTL3D_STATE* pCtl3dState = _afxCtl3dState;
if (pCtl3dState->m_pfnAutoSubclass != NULL)
(*pCtl3dState->m_pfnAutoSubclass)(AfxGetInstanceHandle());
// allocate thread local _AFX_CTL3D_THREAD just for automatic
//termination
_AFX_CTL3D_THREAD* pTemp = _afxCtl3dThread;
pTemp; // avoid unused warning
#endif
}
}
之后是theApp的InitApplication函数,由于程序并没有改写该函数,一次相当于调用CWinApp::InitApplication,其代码位于APPCORE.CPP中,如下所列:
BOOL CWinApp::InitApplication()
{
if (CDocManager::pStaticDocManager != NULL)
{
if (m_pDocManager == NULL)
m_pDocManager = CDocManager::pStaticDocManager;
CDocManager::pStaticDocManager = NULL;
}
if (m_pDocManager != NULL)
m_pDocManager->AddDocTemplate(NULL);
else
CDocManager::bStaticInit = FALSE;
return TRUE;
}
这是MFC为了内部管理所作的工作,我们不必理会。
接下来是pApp->InitInstance,CWinApp类中此函数是虚函数,由于我们的程序改写了该函数,所以现在等于调用我们自己的InitInstance,我们的程序也将从这里开始自己主窗口的生命。我生成的一个简单程序的这一段代码如下:
BOOL CTestApp::InitInstance()
{
AfxEnableControlContainer();
#ifdef _AFXDLL
Enable3dControls();
#else
Enable3dControlsStatic();
#endif
SetRegistryKey(_T("Local AppWizard-Generated Applications"));
LoadStdProfileSettings();
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CTestDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CTestView));
AddDocTemplate(pDocTemplate);
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
if (!ProcessShellCommand(cmdInfo))
return FALSE;
m_pMainWnd->ShowWindow(SW_SHOW); //这两行与SDK程序
m_pMainWnd->UpdateWindow(); //极为相似
return TRUE;
}
标为红色的一句调用了CMainFrame的构造函数,而在此构造函数中又有堆Create函数的调用,该函数用于创建窗口,共有8个参数。声明如下:
BOOL Create(LPCTSTR lpszClassName,LPCTSTR lpszWindowName,
DWORD dwStyle=WS_OVERLAPPEDWINDOW,
const RECT& rect=rectDefault,
CWnd* pParentWnd=NULL,
LPCTSTR lpszMenuName=NULL,
DWORD dwExStyle=0,
CCreateContext* pContext=NULL);
所使用的窗口类名称可以作为其第一个参数传入,也可以使用NULL,表示以MFC内建窗口类产生一个标准的外框窗口;第二个参数是窗口标题栏中显示的文
字;第三个参数为窗口风格;第四个参数指定窗口位置和大小;第五个参数指定父窗口,没有父窗口则使用NULL;第六个参数指定菜单,使用在RC资源文件中
定义的菜单名填写;第七个参数为扩展风格,找支持Windows
3.1以后的版本;第八个参数为一个之乡CCreateContext结构的指针,MFC使用它在具备Document/View结构的应用程序中初始化
外框窗口。
SDK程序设计中要求的对窗口类的注册工作在MFC中也是必需的,并且它是由Create函数在创建窗口之前触发的操作,但其中间过程较为复杂,如果要解
释清楚需要向本文加入大量源代码,限于篇幅本文将不介绍此部分,有兴趣的读者可以阅读侯俊杰先生的《深入浅出MFC》P283-P289,有相当详尽的描
述和分析。
窗口的显示和更新较为简单,主要就是后边标红的两行,因此现在我们要把重心放在CWinApp::RUN函数上,其代码位于APPCORE.CPP中,列出如下:
int CWinApp::Run()
{
if (m_pMainWnd == NULL && AfxOleGetUserCtrl())
{
// Not launched /Embedding or /Automation, but has no main window!
TRACE0("Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application.\n");
AfxPostQuitMessage(0);
}
return CWinThread::Run();
}
可以看到,CWinApp::Run事实上指向CWinThread::Run,它位于THRDCORE.CPP中,代码如下:
int CWinThread::Run()
{
ASSERT_VALID(this);
// 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(&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))
{
bIdle = TRUE;
lIdleCount = 0;
}
} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
}
ASSERT(FALSE); // not reachable
}
PumpMessage代码如下:
BOOL CWinThread::PumpMessage()
{
ASSERT_VALID(this);
if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
{
return FALSE;
}
// process this message
if (m_msgCur.message != WM_KICKIDLE
&& !PreTranslateMessage(&m_msgCur))
{
::TranslateMessage(&m_msgCur);
::DispatchMessage(&m_msgCur);
}
return TRUE;
}
这俨然就是原来的WndProc的一个翻版,很明显正是有着一部分代码来负责整个程序的消息获取、解释、判断和分派的,我们可以通过对它的适当改写来使
得程序的消息流动按我们的意愿进行,另外要使程序能处理空闲时间(Idle
Time)也要从改写该函数开始。而在对消息进行了分派之后就是由早已架设好的由前面曾提到的那些宏所实现的消息映射来驱动整个程序了,由于我计划将消息
映射方在后面的章节再来介绍,因此我们对于程序初生的剖析到此也就告一段落了,下面我们来看一看程序的死亡,也就是退出过程。
第四节、退出
MFC程序的死亡相对于初生来说要简单的多,主要是以下几步:
1.使用者通过点击File/Close或程序窗口由上角的叉号发出WM_CLOSE消息。
2.程序没有设置WM_CLOSE处理程序,交给默认处理程序。
3.默认处理函数对于WM_CLOSE的处理方式为调用::DestoryWindow,并因而发出WM_DESTORY消息。
4.默认的WM_DESTORY处理方式为调用::PostQuitMessage,发出WM_QUIT。
5.CWinApp::Run收到WM_QUIT后结束内部消息循环,并调用ExinInstance函数,它是CWinApp的一个虚拟函数,可以由用户重载。
6.最后回到AfxWinMain,执行AfxWinTerm,结束程序。
以上就是一个MFC程序的初始化和最后退出的全过程,对于阅读本文的读者我深表感谢,同时若文中有什么地方叙述有错误或者读者仍然存有疑问,请去FrontFree论坛提问,我将尽快回答。在后面的章节中我们将继续探索MFC内部机制和实现方法,希望各位继续支持。