• 从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

  • 相关阅读:
    [CTF隐写]png中CRC检验错误的分析
    Bugku
    Bugku
    【CTF 攻略】CTF比赛中关于zip的总结
    sqlserver中利用Tran_sql把逗号分隔的字符串拆成临时表
    H5摇一摇遇到的问题
    C# MVC 微信支付之微信模板消息推送
    各种大型网站技术架构
    ORM框架详解
    显示实现接口
  • 原文地址:https://www.cnblogs.com/hanawasakuraki/p/9499416.html
Copyright © 2020-2023  润新知