Win32應用程序有條明確的主線:
(1) 進入WinMain函數
(2) 設計一個Window
(3) 注冊這個Window
(4) 建立這個Window
(5) 顯示和更新這個Window
(6) 進入消息循環
好,我就先找WinMain函數吧。
我在C:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\src\mfc的appmodul.cpp的23行中找到了以下代碼:
extern "C" int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine, int nCmdShow)
#pragma warning(suppress: 4985)
{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
_tWinMain是一個宏,詳細為: #define _tWinMain WinMain
所以這個確實是我們要找的WinMain函數
從代碼中看出,WinMain將參數全部交給AfxWinMain,來處理。
好,我又找AfxWinMain這個函數。
我在winmain.cpp的19行找到了AfxWinMain函數。
代碼:
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL); // ASSERT在程序運行時它計算括號內的表達式,如果表達式為FALSE (0),
// 程序將報告錯誤,并終止執行。如果表達式不為0,則繼續執行后面的語句。
// ASSERT只有在Debug版本中才有效,如果編譯為Release版本則被忽略。
// assert()的功能類似,它是ANSI C標準中規定的函數,它與ASSERT的一個重要區別是可以用在Release版本中。
int nReturnCode = -1;
// AfxGetThread和 AfxGetApp 都是全局函數
CWinThread* pThread = AfxGetThread(); // 獲得正在執行的線程,Must be called from within the desired thread.
CWinApp* pApp = AfxGetApp(); // 獲得A pointer to the single CWinApp object for the application
// AFX internal initialization
// This function is called by the MFC-supplied WinMain function, as part of the CWinApp initialization of a GUI-based
// application, to initialize MFC.
if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
goto InitFailure;
// App global initializations (rare)
if (pApp != NULL && !pApp->InitApplication()) //InitApplication已經過時,用InitInstance代替,完成MFC內部管理方面的工作
goto InitFailure;
// Perform specific initializations
if (!pThread->InitInstance()) // 初始化Instance,在每個 a copy of the program runs的時候,虛函數
{
if (pThread->m_pMainWnd != NULL)
{
TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n");
pThread->m_pMainWnd->DestroyWindow(); // m_pMainWnd holds a pointer to the application's main window.返回一個CWnd.
// cWnd Destroys the attached Windows window.
}
nReturnCode = pThread->ExitInstance(); // to exit this instance of the thread
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函數后,看了下MFC為我生成的類:
1. CTestApp 2. CTestView 3. CMainFrame 4. CTestDoc 5. CAboutDlg
查看CTestApp.cpp,發現了一個全局的CTestApp theApp,因為全局對象必須在main函數之前產生并初始化,所以應用程序調用的順序應該是
CTestApp的構造函數 -> WinMain函數
又發現class CTestApp : public CWinApp,子類的構造函數在父類的構造函數調用之后調用,所以就搜索CWinApp吧。
在appcore.cpp的368行發現以下代碼:
CWinApp::CWinApp(LPCTSTR lpszAppName) // 此處的lpszAppName有個默認參數NULL
{
if (lpszAppName != NULL)
m_pszAppName = _tcsdup(lpszAppName); // 為lpszAppName分配內存
else
m_pszAppName = NULL;
// initialize CWinThread state
AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();
ENSURE(pModuleState);
AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;
ENSURE(pThreadState);
ASSERT(AfxGetThread() == NULL);
pThreadState->m_pCurrentWinThread = this; // 如果有子類繼承了CWinApp, this就是子類
ASSERT(AfxGetThread() == this);
m_hThread = ::GetCurrentThread();
m_nThreadID = ::GetCurrentThreadId();
// initialize CWinApp state
ASSERT(afxCurrentWinApp == NULL); // only one CWinApp object please
pModuleState->m_pCurrentWinApp = this; // 如果有子類繼承了CWinApp, this就是子類
ASSERT(AfxGetApp() == this);
// in non-running state until WinMain
m_hInstance = NULL;
m_hLangResourceDLL = NULL;v
m_pszHelpFilePath = NULL;
m_pszProfileName = NULL;
m_pszRegistryKey = NULL;
m_pszExeName = NULL;
m_pRecentFileList = NULL;
m_pDocManager = NULL;
m_atomApp = m_atomSystemTopic = NULL;
m_lpCmdLine = NULL;
m_pCmdInfo = NULL;
// initialize wait cursor state
m_nWaitCursorCount = 0;
m_hcurWaitCursorRestore = NULL;
// initialize current printer state
m_hDevMode = NULL;
m_hDevNames = NULL;
m_nNumPreviewPages = 0; // not specified (defaults to 1)
// initialize DAO state
m_lpfnDaoTerm = NULL; // will be set if AfxDaoInit called
// other initialization
m_bHelpMode = FALSE;
m_eHelpType = afxWinHelp;
m_nSafetyPoolSize = 512; // default size
}
然后是CTestApp的構造函數的調用。
在CTestApp的聲明中,它重寫了InitInstance函數,如下:
BOOL CTestApp::InitInstance()
{
AfxEnableControlContainer(); //Call this function in your application object's InitInstance function
//to enable support for containment of ActiveX controls
// Standard initialization
// If you are not using these features and wish to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you do not need.
// In MFC 5.0, Enable3dControls and Enable3dControlsStatic are obsolete because their functionality is incorporated
// into Microsoft's 32-bit and 64-bit operating systems.
#ifdef _AFXDLL
Enable3dControls(); // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif
// Change the registry key under which our settings are stored.
// TODO: You should modify this string to be something appropriate
// such as the name of your company or organization.
SetRegistryKey(_T("Local AppWizard-Generated Applications"));
LoadStdProfileSettings(); // Load standard INI file options (including MRU)
// 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(CTestDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CTestView));
AddDocTemplate(pDocTemplate);
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo; // 對命令查詢分析
ParseCommandLine(cmdInfo);
// Dispatch commands specified on the command line
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;
}
有了WinMain函數,也找到了顯示和更新窗口的語句,但是從哪里開始設計窗口,注冊窗口,建立窗口呢?
我又搜索了WNDCLASS,在wincore.cpp的4495行發現了與設計窗口時很像的函數BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister)
發現MS已經在里面為我注冊了一些窗口,我只要選擇自己想要的樣式就可以了。
那么如何建立一個窗口呢?我又搜索了CreateWindow,在wincore.cpp的675行中有個BOOL CWnd::CreateEx函數。
里面有調用CreateWindowEx。這個函數還調用了一個叫PreCreateWindow的函數,這個函數主要是確定在建立窗口之前,確保要建立的窗口已經注冊了。
好了,一切都準備好了。最后就是進入消息循環。