|
引子:
如果你的程序足够的长,你会发现你所写的程序通常有一些通用的代码例程。在你重新写一个新程序时每次重新写这些代码时都很浪费你的时间。返回到先前的DOS,程序员将通用的例程存放到一个或多个库文件中。当他们想使用这些函数时,他们就把库文件连接到目标文件中并且链接器从链接库中导出这些函数并插入到最后的可执行文件中去。这个过程被称为静态链接。C的实时运行库就是很好的例子。这种方法的缺点是在你的每个程序中都有同样的调用函数。你的磁盘空间都被浪费在了存储同样的函数拷贝上了。但是对于dos程序来说,这种方法被彻底的接受,是因为以前在内存中通常是只有一个程序在运行。因此对于宝贵的内存资源没有浪费。
这种情形在同时可以运行多个程序的windows下开始变得饱受批评。如果你的程序很大的话内存很快就会被吃掉。对于这类问题windows有一种解决方法:动态链接库(dynamic link libraries)。动态链接库是一类函数的通用池。即使你的程序有好几个实例在同时运行,windows不会加载好几个DLL的拷贝到内存中,仅仅会拷贝你程序所要使用的一个DLL到内存中。并且我应该澄清这一点。事实上,所有使用同一个DLL的进程都有一个他们自己的一份关于dll的拷贝。看起来好像是在内存中有很多份DLL的拷贝,但事实上,windows通过页面调度的方式使所有进程共享同一个DLL代码。所以在物理内存中,只有一份DLL代码的拷贝。然而,每个进程有其自己唯一的DLL的数据区域。
应用程序实时连接一个DLL与旧方式的静态链接库不同。这也就是为什么称其为动态链接库的原因。在你的程序不再需要这个DLL时可以实时的将其卸载掉。如果程序是唯一使用此DLL的程序,他将会立即从内存中卸载掉。但是如果还有其他的程序在使用此DLL,DLL会一直保留在内存中,直到最后一个使用的程序卸载时,DLL才真正从内存中卸载!
可是,链接器为最后的可执行文件执行地址修正是很困难的一项工作。以前它不能“导出”函数并把函数插入到最后的可执行文件中,不知何故它必须存放关于DLL的足够的信息并且函数存入最后的可执行文件对于链接器来说应该能实时的定位和加载正确的DLL。这也正是输入库的来历。一个输入库包含了DLL自己描绘的信息。链接器能从输入库中获取它想要得到的信息并把这些信息填写到可执行文件中。当windows加载器将程序加载到内存时,加载器了解了程序要连接一个DLL,所以加载器查找到那个DLL并把DLL映射到进程地址空间中并为要调用的DLL中函数进行地址修正。你可能加载DLL不是依赖使用windows加载器来进行。这种方式有它的好处和坏处: 1、它不需要一个输入库,所以你可以加载和使用任何的DLL,即使DLL没有DLL输入库。可是,你依然遥知道其中包含的函数中提供多少个参数。 2、当你使用加载器为你的程序加载DLL时,如果加载器不能找到DLL,它将会提示“一个请求的DLL文件xxxxx.dll丢失!”!,即使在调用的DLL没有核心的操作你的程序也无法运行。如果程序自己加载DLL,当DLL没有找到并且其中的调用没有核心的操作,你的程序可以给客户一个提示,提示这个事实并且继续执行。 3、你可以调用没有被包含在输入库中“非正式说明”的函数,如果你知道关于此函数的足够的信息。 4、如果你使用LoadLibrary,你必须为你的要调用的每个函数调用GetProcAddress。GetProcAddress用于返回一个特定的DLL中函数的入口点地址,所以你的代码可能稍微大一点但并不是很大。
DLL的代码(名字为DLLExam.asm):
.386 .model flat,stdcall option casemap:none
include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib
.data MsgTitle db "Call TestFunction",0 MsgComment db "Call TestFunction successful!",0 .code DllEntry proc hInstDLL:HINSTANCE,reason:DWORD,reserved1:DWORD mov eax,TRUE ret DllEntry endp
TestFunction proc invoke MessageBox,NULL,addr MsgTitile,addr MsgComment,MB_OK ret TestFunction endp
End DllEntry
相关def文件内容(名字为DLLExam.def):
LIBRARY DLLExam EXPORTS TestFunc
调用例程代码(CallDll.asm):
.386 .model flat,stdcall option casemap:none
include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib
.data libName db "DLLExam.dll",0 FunctionName db "TestFunc",0 DllNotFound db "Can not Load library!",0 AppName db "Load library Exam",0 FunctionNotFound db "Function Not found,call failed!",0
.data? hLib dd ? TestFunctionAddr dd ?
.code start: invoke LoadLibrary,addr libName .if eax == NULL invoke MessageBox,NULL,addr DllNotFound,addr AppName ,MB_OK .else mov hLib,eax invoke GetProcAddress,hLib,addr FunctionName .if eax == NULL invoke MessageBox,NULL,addr FunctionNotFound, addr AppName,MB_OK .else mov TestFunctionAddr,eax call [TestFunctionAddr] .endif invoke FreeLibrary,hLib invoke CloseHandle,hLib .endif invoke ExitProcess,NULL end start
注意:
在def中关于接口函数的声明中,不要使用EXPORTS TestFunc @1 noname的声明,否则将无法直接调用成功。
|