5.4.1 模块的概念
在线程中调用API函数的时候,代码到哪里去执行了呢?比如,在一条语句中调用了MessageBox函数,然后Windows就会弹出了一个对话框。可以肯定,在程序中并没有此函数的实现代码,而用于程序执行的代码最后又都会被链接成.EXE文件,所以最终的可执行文件中没有MessageBox函数的实现代码。这就说明,应用程序在启动的时候,不但加载了.EXE文件,还加载了包含API函数实现代码的文件。每一个被加载到内存中的文件称为一个模块。
一般进程是由多个模块组成的。通常应用程序都是通过模块句柄来访问进程中的模块。事实上,模块句柄的值就是该模块映射到进程中的地址。例如WinMain函数中的第一个参数hInstance,这就是主程序模块的句柄,即.EXE文件映射到内存后的地址。
使用ToolHelp函数可以很容易地查看一个进程都加载了哪些模块,例如下面的代码打印出了当前进程中模块的一些信息。 05ProcessModule
#include <iostream>
#include <windows.h>
#include <tlhelp32.h>
using namespace std;
void main()
{
MODULEENTRY32 me32 = { 0 };
HANDLE hModuleSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
if(hModuleSnap == INVALID_HANDLE_VALUE)
return;
me32.dwSize = sizeof(MODULEENTRY32);
if(::Module32First(hModuleSnap, &me32))
{
do
{
cout << me32.szExePath << "\n";
cout << " 模块在本进程中的地址:" << me32.hModule << "\n";
}
while(::Module32Next(hModuleSnap, &me32));
}
::CloseHandle(hModuleSnap);
}
首先,用CreateToolhelp32Snapshot函数给当前进程拍一个快照,TH32CS_SNAPMODULE参数指定了快照的类型,第二个参数指定了目标进程的ID号。然后用Module32First和Module32Next函数遍历快照中记录的模块信息,运行结果如图5.8所示。 这说明05ProcessModule进程是由3个模块组成的:05ProcessModule.exe(主程序模块)、ntdll.dll和kernel32.dll。这些模块都有一些不同于其他模块的属性,如模块的镜像文件在磁盘的位置,此模块中所有类的列表等,所以有必要定义一个数据结构来维护程序中模块的状态。
5.4.2 模块、线程的状态
模块状态记录了一些模块私有的数据,可以用AFX_MODULE_STATE类来描述它。
class CWnd;
class _AFX_THREAD_STATE : public CNoTrackObject
{
public:
AFX_MODULE_STATE* m_pModuleState;
TCHAR m_szTempClassName[96];
CWnd* m_pWndInit;
HHOOK m_hHookOldCbtFilter;
MSG m_lastSendMsg;
};
EXTERN_THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState);
_AFX_THREAD_STATE* AfxGetThreadState();
同样,每创建一个线程就创建一个新的_AFX_THREAD_STATE对象,并用CThreadLocal类模板保存这个线程状态对象的指针。
_AFX_THREAD_STATE* AfxGetThreadState()
{
return _afxThreadState.GetData();
}
THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState);
m_pModuleState成员保存了线程的模块状态,所以取得模块状态的代码如下。
AFX_MODULE_STATE _afxBaseModuleState;
AFX_MODULE_STATE* AfxGetAppModuleState()
{
return &_afxBaseModuleState;
}
AFX_MODULE_STATE* AfxGetModuleState()
{
_AFX_THREAD_STATE* pState = _afxThreadState.GetData();
AFX_MODULE_STATE* pResult;
if(pState->m_pModuleState != NULL)
pResult = pState->m_pModuleState;
else
pResult = AfxGetAppModuleState();
ASSERT(pResult != NULL);
return pResult;
}
可以认为_afxBaseModuleState是进程模块状态的指针,是进程私有变量。这样一来,应用程序可以设置自己的模块状态,也可以使用这个全局的模块状态。由于现在所讨论的程序都是基于单模块的,所以一般都直接使用_afxBaseModuleState来保存模块信息。
在3.4节定义了模块线程状态AFX_MODULE_THREAD_STATE,在此除了要向里面增加新的成员以外,还要对函数AfxGetModuleThreadState进行重新定义。
class CWinThread;
class CHandleMap;
class AFX_MODULE_THREAD_STATE : public CNoTrackObject
{
public:
CWinThread* m_pCurrentWinThread;
CHandleMap* m_pmapHWND;
};
AFX_MODULE_THREAD_STATE* AfxGetModuleThreadState();
句柄映射是模块和线程局部有效的,属于模块-线程状态的一部分。取得模块-线程状态指针的函数应修改如下。
AFX_MODULE_THREAD_STATE* AfxGetModuleThreadState()
{
return AfxGetModuleState()->m_thread.GetData();
}
|