從win32到MFC(二)CWinApp


上一篇文章《從win32到MFC(一)前言》介紹了MFC的入口函數,有一段代碼:

CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();

初次讀到這兩行代碼還是比較混亂,可以推斷AfxGetThread()和AfxGetApp()獲得的CWinThread和CWinApp對象已經在入口函數執行前完成了構造。

寫過MFC程序的開發者應該可以聯想到全局變量 CxxxApp theApp,把AfxGetApp()與全局變量 theApp關聯起來,AfxGetApp()獲取的正是這個對象。

CWinApp的構造:

CWinApp::CWinApp(LPCTSTR lpszAppName)
{
    ......
    // 初始化 CWinThread 狀態
    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;
    ASSERT(AfxGetThread() == this);
    m_hThread = ::GetCurrentThread();
    m_nThreadID = ::GetCurrentThreadId();

    // 初始化 CWinApp 狀態
    ASSERT(afxCurrentWinApp == NULL); // only one CWinApp object please
    pModuleState->m_pCurrentWinApp = this;
    ASSERT(AfxGetApp() == this);
    ......
}

AfxGetThread 和 AfxGetApp 的實現:

CWinThread* AFXAPI AfxGetThread()
{
    AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
    CWinThread* pThread = pState->m_pCurrentWinThread;
    return pThread;
}
_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp()
    { return afxCurrentWinApp; }

#define afxCurrentWinApp    AfxGetModuleState()->m_pCurrentWinApp

通過以上代碼的實現,可以清晰的看到 AfxGetThread 和 AfxGetApp 獲得的指針對象保存在 AFX_MODULE_THREAD_STATE中,由CWinApp在構造函數中初始化。CWinApp繼承自CWinThread,AfxGetThread 和 AfxGetApp 得到的其實是同一個對象。

以上代碼的分析其實只考慮了主線程的情況,事實上在多線程的環境中其他線程執行 AfxGetThread 獲取到的並不是CWinApp,而是當前線程的對象,繼續往下分析 AfxGetModuleThreadState。

AfxGetModuleThreadState的實現:

AFX_MODULE_THREAD_STATE* AFXAPI AfxGetModuleThreadState()
{
    AFX_MODULE_THREAD_STATE* pResult=AfxGetModuleState()->m_thread.GetData();
    ENSURE(pResult != NULL);
    return pResult;
}

AfxGetModuleThreadState 進一步調用 AfxGetModuleState 獲取 AFX_MODULE_THREAD_STATE 指針對象。

AfxGetModuleState的實現:

AFX_MODULE_STATE* AFXAPI AfxGetModuleState()
{
    _AFX_THREAD_STATE* pState = _afxThreadState;
    ENSURE(pState);
    AFX_MODULE_STATE* pResult;
    if (pState->m_pModuleState != NULL)
    {
        // thread state's module state serves as override
        pResult = pState->m_pModuleState;
    }
    else
    {
        // otherwise, use global app state
        pResult = _afxBaseModuleState.GetData();
    }
    ENSURE(pResult != NULL);
    return pResult;
}

AfxGetModuleState 的實現也很簡潔,我們繼續往下分析全局對象 _afxThreadState (在afxstate.cpp 143行)

THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)

#define THREAD_LOCAL(class_name, ident_name) \
 AFX_COMDAT CThreadLocal<class_name> ident_name;

THREAD_LOCAL宏定義展開其實是一個模板類的定義 CThreadLocal。

template<class TYPE>
class CThreadLocal : public CThreadLocalObject
{
// Attributes
public:
    AFX_INLINE TYPE* GetData()
    {
        TYPE* pData = (TYPE*)CThreadLocalObject::GetData(&CreateObject);
        ENSURE(pData != NULL);
        return pData;
    }
    AFX_INLINE TYPE* GetDataNA()
    {
        TYPE* pData = (TYPE*)CThreadLocalObject::GetDataNA();
        return pData;
    }
    AFX_INLINE operator TYPE*()
    { 
        return GetData(); 
    }
    AFX_INLINE TYPE* operator->()
    { 
        return GetData(); 
    }

// Implementation
public:
    static CNoTrackObject* AFXAPI CreateObject()
        { return new TYPE; }
};

可以看到 CThreadLocal 重載了 operator ->,最終調用了基類 CThreadLocalObject 的函數 GetData 來獲取 _AFX_THREAD_STATE,閱讀到這里終於快搞清 CWinThread 的真面目,我們繼續往下分析。

CThreadLocalObject::GetData 的實現:

CNoTrackObject* CThreadLocalObject::GetData(
    CNoTrackObject* (AFXAPI* pfnCreateObject)())
{
    ENSURE(pfnCreateObject);
    
// 初次調用先構造 CThreadSlotData
if (m_nSlot == 0) { if (_afxThreadData == NULL) { _afxThreadData = new(__afxThreadData) CThreadSlotData; ENSURE(_afxThreadData != NULL); } m_nSlot = _afxThreadData->AllocSlot(); ENSURE(m_nSlot != 0); } CNoTrackObject* pValue = static_cast<CNoTrackObject*>(_afxThreadData->GetThreadValue(m_nSlot)); if (pValue == NULL) { // allocate zero-init object pValue = (*pfnCreateObject)(); // set tls data to newly created object _afxThreadData->SetValue(m_nSlot, pValue); ASSERT(_afxThreadData->GetThreadValue(m_nSlot) == pValue); } return pValue; }

最終通過 afxThreadData 來獲取對象,Windows開發者應該對TLS(線程局部存儲)的概念並不陌生,第一次讀到這里想到了MFC應該通過 TLS 存儲線程相關的環境上下文。

感興趣的讀者可以自行閱讀類 CThreadSlotData 的實現細節,這里不再詳細分析。

總結:

貼了這么多代碼,簡單的總結下流程:

AfxGetApp / AfxGetThread -> AfxGetModuleThreadState -> AfxGetModuleState -> CThreadLocalObject::GetData (_afxThreadState operator->) -> CThreadSlotData::GetThreadValue

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM