随笔-19  评论-2  文章-0  trackbacks-0

2009-10-1

========================================================
《深入解析MFC》笔记 10. MFC的DLL与线程
========================================================

---------------------
概念:
    模块: 
        一段可执行的程序(包括EXE和DLL),其程序代码、数据、资源被加载到内存中,由系统建置一个数据结构来管理它,这就是一个模块。
    进程:
        进程是一堆拥有权(ownership)的集合,进场那个拥有地址空间,动态配置而来的内存、文件、线程和一序列的模块。
    概要:
    · DLL与线程的实现依赖于内部 MFC 状态类,MFC的状态将数据分到不同的逻辑范围中,从而使得线程和 DLL 不会破会对方的数据。
    · 扩展 DLL 仅仅用来扩展已存在的 MFC 应用程序。
    · MFC中,扩展 DLL 被创建时要使用 _AFXDLL标志。
    · 扩展 DLL 有一些资源和其他信息需要在运行时被检索。CDynLinkLibrary是它的辅助类。
   
    · 辅助线程,UI线程 都是用 _beginthreadex()创建、以_endthread()来结束。
    · CWinThread::CreateThread创建线程,并且使用_AfxThreadEntry()来为线程提供执行路径。
    · 核心:CWinThread::Run()
--------------------
MFC状态

3中MFC状态信息类型:
    模块状态、进程状态、线程状态
    win32中,一个模块就是独立于应用程序其他部分而操作的可执行代码。
   
    模块状态,  既可以包含真正的全局状态,也可以包含进程局部或者线程局部的状态,且它可以被快速地切换。
    可把MFC状态理解成应用程序不同部分的局部数据。
        进程状态包含局部于进程和某个模块的数据。模块的状态信息包含局部于该模块的数据,线程的状态信息包含局部于该线程的数据。
       
    ------------------------------
    MFC进程状态       
  
AFX_MODULE_PROCESS_STATE的定义,   AFXSTAT_.H                  《深入解析MFC》P301
    · m_pfnFilterToolTipMessage —— 一个函数指针,指向用于过滤工具提示消息的函数。
    · CTypedSimpleList<CDynLinkLibrary*> m_libraryList —— 附加的MFC扩展DLL链表
    · HINSTANCE m_appLangDLL —— 当前被局部化的资源的实例句柄。
    · COccManager* m_pOccManager —— 指向OLE控件管理对象的指针
    · CTypedSimpleList<COleControlLock*>  m_lockList —— 被锁定的 OLE 控件链表
   
    ------------------------------
    MFC模块状态

AFX_MODULE_STATE 定义,     AFXSTAT_.H            《深入解析MFC》  P302
    · CWinApp*  m_pCurrentWinApp —— 指向该模块的CWinApp对象的指针
    · HINSTANCE  m_hCurrentInstanceHandle —— 模块的实例句柄
    · HINSTANCE  m_hCurrentResourceHandle —— 资源的实例句柄
    · LPCTSTR  m_lpszCurrentAppName —— 当前应用程序的名称
    · BYTE  m_bDLL  —— 指明该模块是否是 DLL 的一个标志。
    · BYTE  m_bSystem —— 指明该模块是否是系统模块的一个标志
    · short  m_fRegisteredClasses —— 用于模块的延迟注册类的位标识。
    · CRuntimeClass*  m_pClassInit —— 指向第一个类的 CRuntimeClass 信息的指针(通常是 m_classList的头)
    · CTypedSimpleList<CRuntimeClass*>  m_classList —— 模块里各个对象的 CRuntimeClass 信息链表
    · COleObjectFactory*  m_pFactoryInit —— 指向第一个 COleObjectFactory对象的指针(通常是 m_factoryList 的头)    
    · CTypedSimpleList<COleObjectFactory*>  m_factoryList —— 模块里各个对象的 COleObjectFactory 对象的链表
    · long m_nObjectCount —— 被锁住的OLE对象的数目。
    · BOOl  m_bUserCtrl —— 如果用户有控制权,则设置为 TRUE,否则为FALSE
    · TCHAR  m_szUnregisterList —— 未被注册的类链表
    · WNDPROC  m_pfnAfxWndProc —— 指向模块所有的 AfxWndProc 的指针
    · DWORD   m_dwVesion —— 模块连接所使用的 MFC 版本号
    · m_process —— 进程状态信息  PROCESS_LOCAL( AFX_MODULE_PROCESS_STATE,  m_process )
    · m_thread —— 线程状态信息   THTEAD_LOCAL( AFX_MODULE_THREAD_STATE, m_thread )
   
    -------------------------------
    MFC 线程状态信息                  《深入解析MFC》P305
   
_AFX_THREAD_STATE
    · AFX_MODULE_STATE*  m_pModuleState —— 指向当前模块状态的指针
    · AFX_MODULE_STATE*  m_pPrevModuleState —— 指向下一个模块状态的指针
    · void* m_pSafetyPoolBuffer —— 指向安全缓冲区的指针,它支持强壮的临时对象内存分配
    · AFX_EXCEPTION_CONTEXT  m_exceptionContext —— 当前的异常环境。
    · CWnd*  m_pWndInit —— 一个窗口指针,指向最近hook的窗口
    · CWnd*  m_pAlternateWndInit —— 指向最近被hook的公用对话框窗口的指针
    · DWORD  m_dwPropStyle ——属性页的风格
    · DWORD  m_dwPropExStyle —— 属性页的扩展风格
    · HWND  m_hWndInit —— m_pWndInit 的匹配句柄
    · BOOL  m_bDlgCreate —— 表明某个对话框被创建了,MFC为对话框绘制与普通窗口不同的背景颜色
    · HHOOK  m_hHookOldSendMsg —— 由::SetWindowsHookEx ( WH_CALLWNDPROC ) 返回的前一个句柄的句柄
    · HHOOK  m_hHookOldCbtFilter —— 由::SetWindowsHookEx ( WH_CBT )返回的前一句并的句柄。
    · HHOOK  m_hHookOldMsgFilter —— 由::SetWindowsHookEx ( WH_MSGFILTER )返回的前一个句柄的句柄
    · MSG  m_lastSentMsg —— 发送的最后一个消息
    · HWND  m_hTrackingWindow —— 当前跟踪窗口的句柄
    · HMENU  m_hTrackingMenu —— 当前跟踪菜单的句柄
    · TCHAR  m_szTempClassName —— 在 AfxRegisterWndClass() 中使用的缓冲区
    · HWND  m_hLockoutNotifyWindow —— 在锁定(没有OLE控件)的窗口句柄,如果存在一个的话。
    · BOOL  m_bInMsgFilter —— 表明该线程在一个消息过滤器中的标志。
    · CView*  m_pRoutingView —— 在将消息发送给文档之前,视图所先将自己保存到该变量中。
    · CFrameWnd*  m_bWaitForDataSource —— 之名 ODBC 正在等待的数据
    · CToolTipCtrl*  m_pToolTip —— 指向当前CToolTipCtrl的指针
    · CWnd*  m_pLastHit —— 指向拥有工具提示控件的最后一个窗口的指针
    · int  m_nLastHit —— 最后的点击测试代码(用于工具提示点击测试)
    · TOOLINFO  m_lastInfo —— 最后的工具提示 TOOLINFO结构
    · int  m_nLastStatus —— 最后的浮动状态代码
    · CControlBar*  m_pLastStatus —— 指向最后的浮动状态控制条的指针
   
    ----------------------------------------------
    MFC状态之间的联系               《深入解析MFC》P305
   
    当MFC需要到达当前的 _AFX_THREAD_STATE时,调用 AfxGetThreadState()
    THREAD_LOCAL( _AFX_THREAD_STATE, _afxThreadState )
    这行代码为每个线程的 TLS 创建一个名为 _afxThreadState 的 _AFX_THREAD_STATE 类,可通过调用 AfxGetThreadState()来访问
    _AFX_THREAD_STATE 记录了指向当前模块状态的指针,名为 m_pModuleState。可通过调用 AfxGetModuleState()得到。
        · 多数情况会得到 _afxThreadState.m_pModuleState 的 AFX_MODULE_STATE
        · 如果为NULL,将有一个全局的进程局部模块状态,PROCESS_LOCAL( _AFX_BASE_MODULE_STATE,  _afxBaseModuleState )
    当MFC是一个DLL时,会通过调用 AfxSetModuleState() 来改变模块的状态。


=================
MFC的DLL
    ---------------------------《深入解析MFC》P307
    USRDLL   与  AFXDLL
   
    USRDLL:
            可被静态“粘贴”到DLL上,可以再DLL使用MFC,而不必要求使用该DLL的应用程序也是用MFC写的。           
    AFXDLL :
            是使用了MFC的DLL,只能用于MFC程序。

    ---------------------------
    DLL 的资源问题
   
        DLL 里有 3 种类型的信息:  
                    资源、      静态的CRuntimeClass指针、           OLE对象厂(object factory)
        MFC 的 DLL 版本程序起点在 DllMain()(当DLL被装载时该函数被调用)
       
            AfxInitExtensionModule( coreDLL, hInstance );
            CDynLinkLibrary*  pDLL  =  new  CDynLinkLibrary ( coreDLL,  TRUE );
           
        装载资源时,通过调用 AfxFindResourceHandle() 来实现
            AfxInitExtensionModule( AFX_EXTENSION_MODULE& state,  HMODULE  hModule )
            {
            //only initialize once
                if ( state.bInitialized ){   //若该模块已经初始化,AfxInitLocalData()被调用来更新 TLS 使用的模块句柄。
                    AfxInitLocalData( hModule );
                    return TRUE;
                }
                state.bInitialized = TRUE;
                    // save the current HMODULE info for resource loading
                state.hModule  = hModule;
                state.hResource = hModule;
                    // save the start of the runtime class list
                AFX_MODULE_STATE* pModuleState  =  AfxGetModuleState();
                state.pFirstSharedClass  = pModuleState->m_classList.GetHead();
                pModuleState->m_classList.m_pHead  =  pModuleState->m_pClassInit;
                    //save the start of the class factory list
                state.pFirstSharedFactory  = pModuleState->m_factoryList.GetHead();
                pModuleState->m_factoryList.m_pHead = pModuleState->m_pFactoryInit;
                return TRUE; 
            }
           
        --------------------------------
        剖析 CDynLinkLibrary                        《深入解析MFC》P310
        class CDynLinkLibrary : public CCmdTarget
        {
            HMODULE  m_hModule;             //
            HMODULE  m_hResource;           //for  shared  resources
            CTypedSimpleList<CRuntimeClass* >  m_classList;
            CTypedSimpleList<COleObjectFactory*>  m_factoryList;
            BOOL   m_bSystem;                   // TRUE  only  for  MFC  DLLs
           
            CDynLinkLibrary*  m_pNextDLL;       //simple singly linked  list
                //implementation
            CDynLinkLibrary::CDynLinkLibrary ( AFX_EXTENSION_MODULE& state,  BOOL  bSystem ){
                m_factoryList.Construct( offsetof ( COleObjectFactory, m_pNextFactory ) );
                m_classList.Construct( offsetof ( CRuntimeClass, m_pNextClass ) );
                    //copy info from  AFX_EXTENSION_MODULE
                m_hResource  = state.hResource;
                m_classList.m_pHead  =  state.pFirstSharedClass;
                m_factoryList.m_pHead  =  state.pFirstSharedFactory;
                m_bSystem  =  bSystem;
                    //insert at the end of the list ( extensions will go in front of core  DLL )
                AFX_MODULE_PROCESS_STATE*  pState  =  AfxGetModuleProcessState();
                AfxLockGlobals ( CRIT_DYNLINKLIST );
                pState->m_libraryList.AddHead ( this );
                AfxUnlockGlobals ( CRIT_DYNLINKLIST );
            }
        }
       
        --------------------------------
        剖析 AfxFindResourceHandle()                《深入解析MFC》P311
            AfxFindResourceHandle()               "DLLINIT.CPP"
           
            首先,如果当前模块不是系统模块,在当前模块的资源句柄(由AfxGetResourceHandle() 函数返回)里查找资源。
            接着,在进程状态 m_libraryList 里遍历 CDynLinkLibrary 链表。若扩展DLL不是系统 DLL,FindResource() 被调用。
                      然后在被进程状态指向的与特定语言相关的DLL里寻找该信息。
            接着,检查当前的模块是否是系统模块,调用FindResource() 搜索资源,最后再次遍历 CDynLinkLibrary链表,在系统扩展DLL里查找该资源
            若没有发现任何东西,返回 AfxGetResourceHandle(),即返回了进程的当前资源句柄。
           

        --------------------------------
        扩展 DLL 初始化与清除
       
        AFXDLL 与 宏
            DECLARE_DYNAMIC( DLL版 和 非DLL版本)  (AFX.H)      《深入解析MFC》P312
            IMPLEMENT_DYNAMIC            (AFX.H)
           
            DLL 和 非DLL之间的差别:
                MFC必须调用能返回静态 CRuntimeClass成员地址的函数,而不是直接存储、访问静态CRuntimeClass数据成员的地址。
               
               
=========================
MFC 线程
    辅助线程            UI 线程
    AfxBeginThread()
        辅助线程:将一个 CWinThread对象和一个指针传递给控制函数(控制函数负责工作的完成)
        UI 线程:  创建一个 CWinThread,将他的 CRuntimeClass 信息传递给 AfxBeginThread()。
       
    --------------------------
    MFC 的辅助线程               
            AfxBeginThread(){
                CWinThread*  pThread = new CWinThread ( pfnThreadProc,  pParam );
                if ( !pThread->CreateThread ( dwCreateFlags | CREATE_SUSPENDED, nStackSize, lpSecurityAttrs ) ){
                    pThread->Delete();          return NULL;
                }
                VERIFY ( pThread->SetThreadPriority ( nPriority ) );
                if( !(dwCreateFlags & CREATE_SUSPENDED )
                    VERIFY ( pThread->ResumeThread() != (DWORD) - 1);
                return  pThread;
            }
   
          
=========================
CWinThread                                          《深入解析MFC》P316
   
    数据成员:
        · CWnd*  m_pMainWnd —— 指向应用程序主窗口的指针, CWinThread需要用该指针来正确地用程序的主窗口。
        · CWnd*  m_bActiveWnd —— 当OLE 服务器在某个地方处于活动状态时,该指针指向包含应用程序的主窗口。
        · BOOL   m_bAutoDelete —— 当该变量为 TRUE时, CWinThread会在线程结束时删除自己。
        · HANDLE  m_hThread —— 被 CWinThread封装的Win32线程的句柄
        · DWORD  m_hThreadID —— 被CWinThread 封装的线程的 Win32 线程 ID。
        · MSG  m_msgCur —— 缓冲当前正被 CWinThread 消息发送其所处理的消息。
        · LPVOID  m_pThreadParams —— 保存pParam参数,他会继续传递给辅助函数(worker function)。
        · AFX_THREADPROC  m_pfnThreadProc —— 指向辅助函数的指针
        · CPoint   m_ptCursorLast —— 最后鼠标移动消息中的 CPoint。被用来在 CWinThread 的空闲时刻过滤掉多与的鼠标移动信息。
        · UINT  m_nMsgLast —— 用来探测两个连续的消息。
       
        · CommonConstruct() —— 供构造函数调用,仅仅将数据成员设置成正确的默认值
        · Delete() —— 如果 m_bAutoDelete为TRUE,则调用删除自身。
       
    ----------------------.                                             《深入解析MFC》P317
    线程的创建——                   “THRDCORE.CPP”
    CWinThread::CreateThread() 第一部分:                      
        BOOL  CWinThread::CreateThread ( DWORD dwCreateFlags,  UINT nStackSize, LPSECURITY_ATTRIBUTES   lpSecurityAttrs ){
                //setup startup structure for thread initialization
            _AFX_THREAD_STARTUP  startup;
            memset ( &startup,  0, sizeof( startup) );
            startup.pThreadState = AfxGetThreadState();         //_AFX_THREAD_STATE
            startup.pThread = this;                     //指向 CWinThread 的后向指针 (back pointer)
            startup.hEvent = ::CreateEvent ( NULL, TRUE, FALSE, NULL);     //线程被成功创建后,hEvent被触发
            startup.hEvent2 = ::CreateEvent ( NULL, TRUE, FALSE, NULL);   //线程被重新启动,hEvent2 被触发
            startup.dwCreateFlags = dwCreateFlags;        //指定线程是否应该被挂起以及其他信息的创建标志。
                //**some event checking and cleanup omitted for brevity
                //create the thread (it may or may not start to run)
            m_hThread = (HANDLE) _beginthreadex ( lpSecurityAttrs, nStackSize, &_AfxThreadEntry,  &startup,
                                                 dwCreateFlags | CREATE_SUSPENDED,  (UINT* ) &m_nThreadID );
            if( m_hThread ==NULL)   
                return FALSE;
        }
        线程创建之后:
                //CWinThread::CreateThread()  continued...
            ① ResumeThread();
            ::WaitForSingleObject ( startup.hEvent,  INFINITE );
            ② ::CloseHandle ( startup.hEvent );
                //if created suspended , suspend it until resum thread wakes it up
            if( dwCreateFlags & CREATE_SUSPENDED )
                ::SuspendThread ( m_hThread );
            if( startup.bError )
                //**Error cleanup omitted - lots of frees and closes <g>
                // allow thread to continue, once resumed (it may already be resumed)
                ::SetEvent ( startup.hEvent2 );
            return TRUE;
       
        一个最初调用CreateThread() 得到的线程(父线程),另一个处在幼稚期的线程。
            到①时调用::ResumeThread( m_hThread ),这是父进程暂停,它等待_AFX_THREAD_START::hEvent。
            幼稚期线程开始运行,开始的位置在 _beginthreadex()调用告诉的位置:_AfxThreadEntry()
    ⊙﹏⊙b      _AfxThreadEntry 简称为 “_ATE”         《深入解析MFC》P319
        1、_ATE将它的 _AFX_THREAD_STARTUP 参数的 pThread 域保存到真正的 CWinThread 指针里。
        2、然后调用 AfxGetThreadState(),它会返回父线程的状态,复制大部分父线程的状态信息,用_AFX_THREAD_STARTUP的参数来修改模块状态。
                新的子线程从父线程那里”继承“到了模块状态信息。
        3、再调用 AfxInitThread(),该函数为该线程创建了一个消息队列以及一些消息过滤器。
        4、在AfxInitThread()执行完成后,_ATE创建一个局部的 CWnd对象,并将它贴附到当前的主窗口里。
                即将它贴附到 CWinApp::m_pMainWnd::m_hWnd,然后使得 CWinThread::m_pMainWnd指针指向它的地址。
        5、hEvent2句柄被从 _AFX_STARTUP_THREAD指针里拷贝出来。
        6、准备通知父线程子线程已准备好,信号被转换成时间,使得父线程里的 ::WaitForSingleObject() 被启动。真正的函数调用是 ::SetEvent(startup->hEvent)
        7、_ATE调用 ::WaitForSingleObject( hEvent2, INFINITE ) 将新的 成人线程休眠,直到父线程发出最后的信号       
            这时,在调用完::WaitForSingleObject()后,回到父线程,即回到②。
            父线程调用::CloseHandle() 为它的子女清除 hEventHandle,若子线程以暂停方式被调用,则在子线程调用::SuspendThread()。
        8、WaitForSingleObject()调用完成后,子线程向它的父线程说再见,并关闭了 hEvent2 记录的句柄
        9、如果CWinThread::m_pfnThreadProc非空,子线程会意识到自己是 辅助线程,并且通过控制传递给 m_pfnThreadProc来开始工作
        10、若m_pfnthreadproc为空,则为一个UI 线程。
       
            _AfxThreadEntry() 中与 UI 有关的部分          "THRDCORE.CPP"
                    //Initialization and synchronization with mom before here.
                if ( pThread->m_pfnThreadProc != NULL )
                    nResult = pThread->ExitInstance();
                else if ( ! pThread->InitInstance() )         // InitInstance() 被CWinThread的派生类重载
                    nResult = pThread->ExitInsance();
                else
                    nResult = pThread->Run();             // 通常情况调用 CWinThread::Run()
                    //cleanup and shutdown the thread
                threadWnd.Detach();                             //将局部的 CWnd 主窗口对象分离出来
                AfxEndThread( nResult );                        //最终调用 _endthreadex().
                return 0;           //not reached
    ----------------
    MFC   UI 线程                                                                《深入解析MFC》P320

        辅助线程仅需向 CWinThread 提供一个指向某个函数的指针,
        UI 线程 需要从CWinThread 派生。
            · ExitInstance() —— 当线程结束时执行一些清理工作。
            · InitInstance() —— 执行线程实例的初始化工作。
            · OnIdle() —— 执行与线程有关的空闲时的处理工作。
            · PreTranslateMessage() —— 消息过滤器
            · Run() —— 消息泵(非MFC消息的循环处理)
        CWinApp 是一个UI线程
                   
    -----------------------------
    CWinThread::Run()                        “THRDCORE.CPP”
  
    int CWinThread::Run(){
        BOOL bIdle = TRUE;
        LONG  lIdleCount = 0;
        for( ; ; ){
                //phase1: check to see if we can do idle work
            while (bIdle && ! ::PeekMessage ( &m_msgCur, NULL, NULL, NULL, PM_NOREMOVE ) ){
                if ( ! OnIdle ( lIdleCount ++ ) )
                    bIdle = FALSE;                      // assume "no idle " state
            }
                //phase 2: pump messages while available
            do  {
                  if ( !PumpMessage() )
                      return ExitInstance();
                  if ( IsIdleMessage ( &m_msgCur ) ){
                      bIdle = FALSE;
                      lIdleCount = 0;
                  }
            }while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE ) );
        }
    }
        通过bIdle标志 和 LONG变量 lIdleCount 连接两个阶段。
    · 阶段一 —— while循环,当线程的消息队列里没有任何消息时,它处在闲置状态。
    · 阶段二 —— do-while 循环,当有消息需要被分发时,它进行消息的分发。
   
   
        ●  阶段一: 空闲处理
        通过调用OnIdle() 来告诉用户没有执行任何操作,线程可以做一些清理工作或其他。闲置操作完成后,返回 0 ;
        OnIdle()的参数用来指示线程已经有多长时间处在非活动状态了,这样,可以对空闲处理做一些优先排序工作。
       
    CWinThread::OnIdle()                            “THRDCORE.CPP”
   
    BOOL CWinThread::OnIdle ( LONG lCount )
    {
            if ( lCount <= 0 ) {
                    //send  WM_DILEUPDATECMDUI  to the main window
                    //send  WM_IDLEUPDATECMDUI  to all  frame  windows
            }
            else if ( lCount >= 0 ){
                    //Call AfxLockTempMaps / AfxUnlockTempMaps to free maps, DLLS, etc...
            }
            return  lCount < 0;     //nothing more to do if lCount >= 0
    }
        若调用 OnIdle( -1 ),强制执行一次 UI 更新,
   
        ●  阶段二 : 消息的分发
       
            do-while 循环中,调用 PumpMessage() 分发消息,然后调用 IsIdleMessage() 来确认当前正在被处理的消息是否是空闲消息。

 


 

posted on 2010-03-15 23:27 Euan 阅读(2493) 评论(0)  编辑 收藏 引用 所属分类: windows

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理