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 閱讀(2727)
評論(0) 編輯 收藏 引用 所屬分類:
windows