DLL基础


DLL基础
2010年06月29日
  自从Microsoft Windows第一个版本的诞生之日起,动态链接库(dynamic-link library,DLL)一直以来都是该操作系统的基石。Windows应用程序编程接口(application programming interface,API)提供的所有函数都包含在DLL中,其中3个最重要的DLL分别是:Kennel32.dll,包含的函数用来管理内存、进程、线程;User32.dll,包含的函数用来执行与用户界面相关的任务,如创建窗口和发送消息;GDI32.dll,包含的函数用来绘制图像和显示文字。
  Windows还提供了其它一些DLL,用来执行更加专门的任务。例如:AdvAPI32.dll包含的函数与 对象的安全性、注册表的操控以及事件日志有关。ComDlg32.dll包含的一些常用的对话框(如:打开和保存文件对话框),ComCtl32.dll支持所有常用的窗口控件。为什么使用DLL在此就不做过多的描述了。
  1、DLL和进程的地址空间
  创建DLL通常比创建应用程序容易,因为DLL通常由一组可供任何应用程序使用的独立函数组成。在DLL中,通常没有用来处理消息循环或创建窗口的代码。DLL只不过是一组源代码模块,每个模块包含一些可供应用程序(可执行文件)或其它DLL调用的函数。在所有的源文件编译完成之后,链接器会像链接应用程序的可执行文件那样,对它们进行链接,但在创建DLL的时候,我们必须给链接器指定DLL开关(如:_declspec(dllexport))。 这个开关会使链接器在生成的DLL文件映像中保存一些与可执行文件略微不同的信息,这样操作系统的加载程序就能够将该文件映像识别为DLL,而不会将它识别为应用程序。
  在应用程序(或其它DLL)能够调用一个DLL中的函数之前,必须将该DLL的文件映像 映射到 调用进程的地址空间中。我们可以通过2中方法来达到这种目的:隐式 载入时链接(implicit load-time linking)或 显式运行时链接(explicit run-time linking)。
  一旦系统将一个DLL的文件映像 映射到调用进程的地址空间中之后,进程中的所有线程就可以调用该DLL中的函数了。事实上,该DLL几乎完全丧失了它的DLL身份:对调用进程中的线程来说,该DLL中的代码和数据就像是一些附加的代码和数据,碰巧被放在进程地址空间中。当线程调用DLL中的一个函数的时候,该函数会在线程中取得传给他的参数,并使用线程栈来存放它需要的局部变量。此外,该DLL中的函数创建的任何对象都为调用线程或调用进程所拥有----DLL绝对不会拥有任何对象。
  举个例子,如果DLL中的一个函数调用了VirtualAlloc,系统就会从调用进程的地址空间中预定地址空间区域。如何稍候从进程的地址空间中撤销对DLL的映射,那么这块地址空间区域仍将保持被预定状态,因为虽然该区域事实上是由DLL中的函数所预定的,但系统并不会对此进行记录。被预定的区域被进程所拥有,只有当线程调用了VirtualFree函数或者当进程终止的时候,该区域才会被释放。
  正如我们所知道的那样,如果运用同一个可执行文件的多个实例,这些实例将不会共享可执行文件中的全局变量和静态变量。Windows通过使用写时复制机制来保证这一点。DLL中的全局变量和局部变量也是通过完全相同的方法来处理的。当一个进程将一个DLL映像文件 映射到自己的地址空间中时,系统也会为全局变量和静态变量创建新的实例。 说明:我们必须理解 一个地址空间是由一个可执行模块和 多个DLL模块构成的,这一点非常重要。这些模块中,有些可能会链接到c/c++运行库的静态版本,有些可能会链接到c/c++运行库的DLL版本,还有一些可能根本就不需要C/C++运行库(如果模块不是用C/C++编写的)。许多开发人员常犯的一个错误就是忘记一个地址空间中可能会存在多个C/C++运行库。分析下面的代码: void EXEFunc() { PVOID pv=DLLFunc(); //access the storage pointed to by pv //Assumes that pv is in EXE's C/C++ run-time heap free(pv); } PVVOID DLLFunc() { return (malloc(100));//Allocate block from DLL's C/C++ run-time heap }  前面的代码能正常工作吗?DLL中的函数分配的内存块是否能被EXE中函数所释放呢?答案是:也许。前面显示的代码没有给我们足够的信息。如果EXE和DLL都链接到C/C++运行库的DLL版本,那么代码将能够正常工作。但是,如果其中之一或2个模块都链接到C/C++运行库的静态版本,free就会失败。开发人员编写出与此类似代码并深受其害,这样的事情我们屡见不鲜。这个问题我们有一个简单的解决办法,即:当一个模块提供一个内场分配函数的时候,它必须同时提供另一个用来释放内存的函数,下面重写刚才的那段代码: void EXEFunc() { PVOID pv=DLLFunc(); //access the storage pointed to by pv //Assumes that pv is in EXE's C/C++ run-time heap free(pv); } PVVOID DLLFunc() { return (malloc(100));//Allocate block from DLL's C/C++ run-time heap } BOOL DLLFreeFunc() { //Free block from DLL's C/C++ run-time heap return (free(pv)); }  这段代码是正确的,并且始终都能正常工作,在编写一个模块的时候,不要忘记其它模块中的函数甚至不是用C/C++编写的,因此,可能不会用malloc和free来进行内存分配。请务必小心,不要再代码中做这样的假设。顺便提一句,同样的道理也适用于C++的new和delete操作符,因为它会在内部调用malloc和free。

猜你喜欢

转载自qsxr76qsxr.iteye.com/blog/1362675
dll