。解析AfxGetApp()函數
本文是學習深入淺出MFC後的第一個筆記,解析AfxGetApp()函數
在AFXWIN.H中是這麼定義的:
CWinApp* AFXAPI AfxGetApp();
那麼AfxGetApp是怎麼擷取當前App的CWinApp類指標呢?
AfxGetApp是一個內嵌函式,其實現如下(在AFXWIN1.INL):
_AFXWIN_INLINE CWinApp *AFXAPI AfxGetApp()
{ return afxCurrentWinApp;}
而afxCurrentWinApp是一個宏,定義在AFXWIN.H中:
#define afxCurrentWinApp AfxGetModuleState()->m_pCurrentWinApp
AfxGetModuleState返回的是一個:AFX_MODULE_STATE類的指標(AFXSTAT_.H):
AFX_MODULE_STATE* AFXAPI AfxGetModuleState();
在AFX_MODULE_STATE類中定義了如下的成員變數:
CWinApp* m_pCurrentWinApp;
HINSTANCE m_hCurrentInstanceHandle;
HINSTANCE m_hCurrentResourceHandle;
LPCTSTR m_lpszCurrentAppName;
BYTE m_bDLL; // TRUE if module is a DLL, FALSE if it is an EXE
BYTE m_bSystem; // TRUE if module is a "system" module, FALSE if not
BYTE m_bReserved[2]; // padding
DWORD m_fRegisteredClasses; // flags for registered window classes
轉了這麼多,自己都迷糊了,AFX_MODULE_STATE什麼時候被初始化了,AfxGetModuleState又都幹了些什麼,不然怎麼可能調用AfxGetModuleState()->m_pCurrentWinApp獲得當前視窗的App呢?最有可能被初始化的地方是在建構函式 之中。而我們擷取的是App類型的指標,而App是繼承之CWinApp類的。因此下面我們看看CWinApp建構函式做了些什麼工作:
CWinApp::CWinApp(LPCTSTR lpszAppName)
{
if (lpszAppName != NULL)
m_pszAppName = _tcsdup(lpszAppName);
else
m_pszAppName = NULL;
// initialize CWinThread state
AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE(); //看,聲明了一個指標
AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;
ASSERT(AfxGetThread() == NULL);
pThreadState->m_pCurrentWinThread = 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; //又將this指標傳給了m_pCurrentWinApp,哦呵呵……
ASSERT(AfxGetApp() == this);
// in non-running state until WinMain
m_hInstance = NULL;
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_nSafetyPoolSize = 512; // default size
}
CWinApp的建構函式申明了一個AFX_MODULE_STATE類的指標,裡面通過this指標填充了AFX_MODULE_STATE指標中的部分內容。下面我們再來看看CWinApp中的_AFX_CMDTARGET_GETSTATE()又是什麼:
#ifdef _AFXDLL
#define _AFX_CMDTARGET_GETSTATE() (m_pModuleState)
#else
#define _AFX_CMDTARGET_GETSTATE() (AfxGetModuleState())
#endif
如果此處不考慮_AFXDLL情況,那麼在CWinApp中將直接調用 AfxGetModuleState()函數,瞧,又是AfxGetModuleState()函數。於是我們可以這麼考慮,在記憶體中有一份全域或靜態 AFX_MODULE_STATE類,AfxGetModuleState只是返回這一份全域指標(猜測)。在CWinApp中通過對AFX_MODULE_STATE中的m_pCurrentWinApp填充this指標後,將使全域的AFX_MODULE_STATE儲存當前WinApp中的CWinApp指標。
其中的特殊指之處在於使用了this指標,當基類被繼承後,this指標將代表繼承類的this指標。因此任何一個CWinApp被繼承後,如繼承類為CMyWinApp,那麼CMyWinApp的地址將被存在AFX_MODULE_STATE的全域變數之中,當使用AfxGetModuleState()函數擷取 AFX_MODULE_STATE時便可擷取當前系統的CMyWinApp指標m_pCurrentWinApp。
下面總結一下:
當前指標通過AfxGetApp()通過返回afxCurrentWinApp,而afxCurrentWinApp宏為AfxGetModule()- >m_pCurrentWinApp,即AfxGetApp()通過返回AfxGetModule()->m_pCurrentWinApp 擷取當前App的指標,而AfxGetModule()->m_pCurrentWinApp指標通過CWinAPP建構函式通過this指標設 置,而this指標恰恰就是當前App的指標(this將轉換為繼承類的指標)。
因此也就解釋了不管當前App被怎麼繼承,AfxGetModule()->m_pCurrentWinApp始終能夠擷取當前App的指標的原因了。