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 阅读(2618)
评论(0) 编辑 收藏 引用 所属分类:
windows