第十九章 DLL基础
本章内容
19.1 DLL和进程的地址空间
19.2 纵观全局
Windows的系统API都包含在DLL中。最重要的3个DLL是Kernel32.dll 包含内存管理,进程和线程。
User32.dll 用户界面相关的任务,创建窗口和发送消息等。
GDI32.dll 包含函数用来绘制图形和显示文字。
windows还有其他DLL用来执行更专门的任务。AdvAPI32.登陆了包含函数与对象安全性,注册表操作以及事件日志相关的函数
ComDlg32.dll 包含常用的对话框(如文件打开和保存文件)
ComCtl32.dll 支持所有常用的窗口控件
为何需要创建自己的DLL?
1)扩展了应用程序的特性。
2)简化了项目的管理
3)有助于节省内存
两个或两个以上应用程序使用同一个DLL,那么该DLL只会载入一次,之后的应用程序可以共享该DLL在内存中的页面。
4)促进了资源的共享
DLL中可以存放一些窗口资源,图标等。
5)促进了本地化
例如一个应用程序可以只包含代码并不含任何用户界面,DLL用来存放本地化的用户界面组件,供应用程序载入并使用。
6)解决平台间的差异
7)用于特殊目的
例如可以通过SetWindowsHookEx和SetWinEventHook来安装某些挂钩函数
对Windows的资源管理器的外壳进行扩展,
网页组件扩展等(ActiveX控件)
19.1 DLL和进程的地址空间
通常DLL没有消息循环和创建窗口的代码。DLL包含了一系列库函数。 连接器链接时使用/DLL开关,会给DLL文件映像保存一些和exe略微不同的信息。
这样操作系统加载程序就能够将该文件映像识别为DLL,而不会将它识别为应用程序。
在使用DLL以前,需要将DLL文件映像映射到调用进程的地址空间中。可以通过隐式载入时链接(implicit load-time linking)
显式运行时链接(explicit run-time linking)
一旦将DLL文件映射到调用进程的地址空间以后,进程中的所有线程就可以调用该DLL函数了。就和调用应用程序自身的函数一样。
另外DLL本身不会拥有任何对象,都为调用进程所拥有。
比如DLL的某个函数中调用了VirtualAlloc,那么会从调用进程的地址空间中预定地址空间区域。如果稍后进程地址空间撤销对DLL的映射。那么这块地址空间区域仍然保持被预定的状态。(因为这块区域的所有者是进程)
DLL库中的全局和静态变量也受写时复制机制保护。这样多个进程载入同一个DLL并不会互相破坏数据。
19.2 纵观全局
DLL中导入函数和变量的模块称为“可执行模块”
导出函数和变量以供可执行文件使用的模块称为“DLL模块” , DLL模块也可以导入一些包含在其他DLL模块中的函数和变量。
如果一个可执行模块要从另一个DLL模块中导入函数和变量,必须先构建该DLL模块,然后再构建可执行模块。
构建DLL的步骤:
1)创建一个头文件,包含了要在DLL中导出的函数原型,结构以及符号等。 DLL的所有源文件都必须包含这个头文件
2)创建CPP文件来实现DLL模块中导出的函数和变量。
3)构建DLL模块的时候,编译器会对每个源文件进行处理并产生一个obj模块(每个源文件对应一个obj模块)
4)所有obj模块都创建完毕以后,连接器会将所有obj模块的内容合并起来,产生一个单独的DLL映像文件。映像文件保护所有DLL的二进制代码以及全局静态变量。
5)连接器检测到DLL的源文件输出了至少一个函数或变量。连接器还会创建一个.lib文件。这个.lib文件非常小,因为它并不包含任何函数或变量。它只是列出了所有被导出的函数和变量的符号名。为了构建可执行模块,这个文件是必须的。
构建可执行模块:
1)所