按惯例,这一篇文章主要还是作者读《深入浅出MFC》整理的一些笔记。
WinMain函数在哪里?
CWinApp和CFrameWnd类:前者代表了程序而本体,后者代表了一个框架窗口。
CwinApp取代了WinMain的地位,而CFrameWnd取代了WndProc的地位。
virtual BOOL InitApplication();
virtual BOOL InitInstance();
virtual int Run();
在“…\VS\IDE\VC\Tools\MSVC\14.34.31933\atlmfc\include\afxwin.h”文件夹下,可以找到CWinApp的类名。在CWinApp类中,主要起作用的就是上述三类函数。
由CWinApp类定义一个theApp变量后,定义在CWinApp类中的各变量都会有一个实际的例程。在“…\VS\IDE\VC\Tools\MSVC\14.34.31933\atlmfc\src\mfc、winmain.cpp”中是MFC实际的winmain程序,之所以在theApp配置中没有明显的winmain函数,是因为MFC单独在此准备了main函数并由链接器直接加到应用程序代码中。
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);//判断上一同句柄进程是否存在。
int nReturnCode = -1;
CWinThread* pThread = AfxGetThread();//获取由进程函数创造的线程
CWinApp* pApp = AfxGetApp();//这是一个全局函数,定义在AFXWIN1.INL中,又进一步定义在AFXWIN.H中
// AFX internal initialization
if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))//AFX初始化
goto InitFailure;
// App global initializations (rare)
if (pApp != NULL && !pApp->InitApplication())//APP初始化
goto InitFailure;
// Perform specific initializations
if (!pThread->InitInstance())//线程初始化
{
if (pThread->m_pMainWnd != NULL)//判断有没有主窗口
{
TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n");
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();
return nReturnCode;
}
应该反过来说,是winmain函数调用了theWinApp类,而不要期望在winapp中找到winmain函数。主函数在判断完没有多开程序进程之后,开始分别进入AFX初始化、APP初始化、线程初始化、执行、关闭流程。
为了加深理解,这里笔者强调,所谓MFC是一个封装完备的类库,就是因为把这些原本你可以操作的东西都隐藏起来了,只让你作用App类,只让你关心各具体组件,不想你关心报错、初始化、WindowsAPI函数调用等其他的问题。
如果你有特殊的程序需求,必须要拆开MFC不可的时候,就沿着这一文件的路径去做吧,这里的winmain函数与winapp类高度绑定。winmain通过各类获取函数间接的控制间接的控制着app类。
AfxWinInit内部初始化
Afx动作记录在APPINIT.CPP中,具体路径参考为:“…\VS\IDE\VC\Tools\MSVC\14.34.31933\atlmfc\src\mfc\appinit.cpp”中(src内文件的风格就是,一个文件做一个函数)。这个函数的主要作用是初始化MFC,如果你调用此函数,应该首先定义一个CWinApp类的实例。
BOOL AFXAPI AfxWinInit(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
_In_z_ LPTSTR lpCmdLine, _In_ 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();//获取当前App类
if (pApp != NULL)
{
// Windows specific initialization (not done if no CWinApp)
pApp->m_hInstance = hInstance;//将各个值赋给当前App类
hPrevInstance; // Obsolete.
pApp->m_lpCmdLine = lpCmdLine;
pApp->m_nCmdShow = nCmdShow;
pApp->SetCurrentHandles();
}
// initialize thread specific data (for main thread)
if (!afxContextIsDLL)
AfxInitThread();//开始线程进程初始化
return TRUE;
}
不过这个AFX的函数在初始化了APP之后还没写完,还要继续初始化线程(thread),所以要去“…\VS\IDE\VC\Tools\MSVC\14.34.31933\atlmfc\src\mfc\thrdcore.cpp”找AfxInitThread函数:
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());
}
}
到此,MFC的初始化工作做完,主要任务是:1.获取当前APP,并将来自系统的程序句柄、程序参数输入给app;2.初始化线程,并将由系统函数开辟的进程或线程赋值告知MFC。
CWinApp类的初始化、实例化
CWinApp类内函数的定义,去往"…\VS\IDE\VC\Tools\MSVC\14.34.31933\atlmfc\src\mfc\appcore.cpp"文件内去找。其中winmain函数里出现的app初始化函数定义如下:
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;
LoadSysPolicies();
return TRUE;
}
显而易见,主要目的,也就是做一些参数赋值,或者是为参数建立内存变量。在完成变量初始化后,开始进行变量实例化:
BOOL CWinApp::InitInstance()
{
InitLibId();
m_hLangResourceDLL = LoadAppLangResourceDLL();
if(m_hLangResourceDLL != NULL)
{
AfxSetResourceHandle(m_hLangResourceDLL);
_AtlBaseModule.SetResourceInstance(m_hLangResourceDLL);
}
// Register the application with the Restart Manager, if supported.
if (SupportsRestartManager())
{
RegisterWithRestartManager(SupportsApplicationRecovery(), _T(""));
}
// If the application is linked statically to MFC, then cleanup of the globals
// does not need to be reference counted. Otherwise, we need to make sure that
// the last CWinApp to exit does the cleanup of all the globals.
#ifdef _AFXDLL
AfxGlobalsAddRef();
#endif
return TRUE;
}
应用程序一定要改写虚函数,因为它在CWinApp内只是各空函数,没有内建动作。