前言
本章主要讲 与DLL相关的各种操作方法。大多数应用程序不一定需要这些方法,但是它们是非常有用的,所以应该对它们有所了解。
一、DLL模块的显式加载和符号链接
如果线程需要调用D L L模块中的函数,那么D L L的文件映像必须映射到调用线程的进程的。
创造DLL:
1)建立带有输出原型/结构/符号的头文件。
2)建立实现输出函数/变量的 C/C++源文件。
3)编译器为每个 C/C++源文件生成.obj模块。
4)链接程序将生成DLL的 .obj模块链接起来。
5)如果至少输出一个函数/变量,那么链接程序也生成 .lib 文件。
创造EXE:
6)建立带有输入原型/结构/符号的头文件(视情况而定)。
7)建立不引用输入函数/变量的 C/C++源文件。
8)编译器为每个C/C++源文件生成 .obj源文件。
9)链接程序将各个 .obj模块链接起来,生成 .exe文件。
注:DLL的lib文件是不需要的,因为并不直接引用输出符号。.exe 文件不包含输入表。
运行应用程序:
10)加载程序为 .exe创建模块地址空进程的主线程开始执行;应用程序启动运行。
显式加载DLL:
11)一个线程调用LoadLibrary
(Ex)函数,将DLL加载到进程的地址空间这时线程可以调用GetProcAddress以便间接引用DLL的输出符号。
加载DLL方法:
- 隐式加载:让应用程序的源代码只引用 D L L中包含的符号。这样,当应用程序启动运行时,加载程序就能够隐含加载(和链接)需要的DLL。
- 显示加载: 在应用程序运行时让应用程序显式加载需要的 D L L并且显式链接到需要的输出符号。换句话说,当应用程序运行时,它里面的线程能够决定它是否要调用 DLL中的函数。该线程可以将D L L显式加载到进程的地址空间,获得 D L L中包含的函数的虚拟内存地址,然后使用该内存地址调用该函数。
显示加载这种方法的优点是一切操作都是在应用程序运行时进行的。
1.1 显式加载DLL模块 LoadLibrary
进程中的线程都可以决定将一个 D L L映射到进程的地址空间,方法是调用下面两个函数中的一个:
HINSTANCE LoadLibrary(PCTSTR pSzDLLPathName); HINSTANCE LoadLibraryEx( PCTSTR pSzDLLPathName, HANDLE hF1le, DWORD dwF1ags );
作用:这两个函数均用于找出用户系统上的文件映像(使用上一章中介绍的搜索算法),并设法将D L L的文件映像映射到调用进程的地址空间中。
pSzDLLPathName:加载的dll文件的路径。
HINSTANCE:用于标识文件映像映射到的虚拟内存地址。如果DLL不能被映射到进程的地址空间,则返回 NULL。若要了解关于错误的详细信息,可以调用GetLastError。
hFile:保留供将来使用,现在必须是 NULL。
dwFlags:必须将它设置为 0 。
或者设置DONT_RESOLVE_DLL_REFERENCES、
LOAD_LIBRARY_AS_DATAFILE 和LOAD_WITH_ALTERED_SEARCH_PATH等标志的一个组合。
DONT_RESOLVE_DLL_REFERENCES
用于告诉系统将D L L映射到调用进程的地址空间中。
通常情况下,当DLL被映射到进程的地址空间中时,系统要调用 DLL中的一个特殊函数,即 DllMain(本章后面介绍)。该函数用于对 D L L进行初始化。
作用:
- DONT_RESOLVE_DLL_REFERENCES标志使系统不必调用DllMain函数就能映射文件映像。
- 当 DONT_RESOLVE_DLL_REFERENCES标志被设定时,系统并不自动将该DLL依赖的其他的DLL加载到进程的地址空间中。
LOAD_LIBRARY_AS_DATAFILE
系统只是将D L L映射到进程的地址空间中,就像它是数据文件一样。系统并不花费额外的时间来准备执行文件中的任何代码。
当一个 D L L被映射到进程的地址空间中时,系统要查看D L L中的某些信息,以确定应该将哪些页面保护属性赋予文件的不同的节。如果设定了LOAD_LIBRARY_AS_DATAFILE标志,系统将以它要执行文件中的代码时的同样方式来设置页面保护属性。
由于下面几个原因,该标志是非常有用的:
- 加载只有资源没有函数的DLL时: 首先,如果有一个 D L L(它只包含资源,但不包含函数),那么可以设定这个标志,使 DLL的文件映像能够映射到进程的地址空间中。
- 调用加载资源的函数: 然后可以在调用加载资源的函数时,使用LoadLibraryEx函数返回的HINSTANCE值。
- 加载EXE访问其中资源: 通常情况下,加载一个.exe文件,就能够启动一个新进程,但是也可以使用 LoadLibraryEx函数将.exe文件的映像映射到进程的地址空间中。借助映射的 .exe文件的HINSTANCE值,就能够访问文件中的资源。
由于.exe文件没有DllMain函数,因此,当调用LoadLibraryEx来加载一个.exe文件时,必须设定LOAD_LIBRARY_AS_DATAFILE标志。
LOAD_WITH_ALTERED_SEARCH_PATH
用于改变 LoadLibraryEx用来查找特定的DLL文件时使用的搜索算法。
如果设定了该标志,那么LoadLibraryEx 函数就按照下面的顺序来搜索文件:
- pszDLLPathName参数中设定的目录。
- 进程的当前目录。
- Windows的系统目录。
- Windows目录。
- PATH环境变量中列出的目录。