【转】动态链接库(Dynamic Link Library)学习笔记这篇写的很好,收藏一下。转载:http://www.cnblogs.com/yxin1
【转】动态链接库(Dynamic Link Library)学习笔记
这篇写的很好,收藏一下。
转载:http://www.cnblogs.com/yxin1322/archive/2008/03/08/donamiclinklibrary.html作者:EricYou?转载请注明出处
???????? 注:本文所写的动态链接库指传统的DLL,并非是.NET中的Assembly.
什么是动态链接和动态链接库
Windows下动态链接库的编写
?{
??return a+b;
?}??_declspec(dllexport) int subtract(int a,int b)
?{
??return a-b;
?}??????? 接下来编译链接,就会在debug目录下生成一个调试版本的动态链接库,该链接库包含了add和subtract两个可供外部调用的函数。我们注意到,在源文件中多了一个没有见过的语句?_declspec(dllexport) ,这个语句的作用就是向编译器指出我需要在生成的动态链接库中导出的函数,没有导出的函数是不能被其他程序调用的。要知道一个动态链接库导出了什么函数,可以在命令提示行用命令"dumpbin -exports DllTest.dll"来查看(也可以用VC工具包中的depends使用程序来查看)。以下是用dumpbin命令查看DllTest.dll而生成的信息:
??? 4420BEA4 time date stamp Wed Mar 22 11:04:04 2006
??????? 0.00 version
?????????? 1 ordinal base
?????????? 2 number of functions
?????????? 2 number of names???? ordinal hint RVA????? name?????????? 1??? 0 0000100A??add@@YAHHH@Z
????????? 2??? 1 00001005??subtract@@YAHHH@Z?? Summary???????? 7000 .data
??????? 1000 .idata
??????? 3000 .rdata
??????? 2000 .reloc
?????? 2A000 .text
???????? 可以看到,我们编写的动态链接库导出了两个函数,分别名为?add@@YAHHH@Z?和??subtract@@YAHHH@Z,为什么名字不是add和subtract呢?这是因为C++为了支持函数的重载,会在编译时将函数的参数类型信息以及返回值类型信息加入到函数名中,这样代码中名字一样的重载函数,在经过编译后就互相区分开了,调用时函数名也经过同样的处理,就能找到对应的函数了。编译器对函数的重命名规则是与调用方式相关的,在这里采用的是C++的默认调用方式。以此对应的还有stdcall方式、cdecl方式、fastcall方式和thiscall方式,不同调用方式的重命名规则不一样。??????? 需要特别说一下的是stdcall方式和cdecl方式:????????stdcall方式(标准调用方式)也即pascal调用方式,它的重命名规则是函数名自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数所占字节数,之所以要跟参数字节数,是因为stdcall采用被调函数平衡堆栈方式,用函数名最后的数字告诉编译器需要为函数平衡的字节数。例如,如果我们的DllTest.dll采用stdcall方式编译的话,导出的函数名将会是?_add@8?和?_subtract@8?,而函数编译后的汇编代码最后一句一定是 ret8。????????cdecl方式即C语言调用方式,它的重命名规则仅仅是在函数名前加下划线(奇怪的是我用vc6编译的c语言函数,名字没有任何改变),因为C语言采用的是调用函数平衡堆栈的方式,所以不需要在函数名中加入参数所占的字节数,这样的堆栈平衡方式也使C语言可以编写出参数不固定的函数;同时C语言不支持函数重载,因此不需要在函数名中加入参数类型信息和返回值类型信息。????????
??????? 动态链接库已经生成了,接下来就是调用的工作了。调用动态链接库有两种方式:隐式调用和显式调用,下面我们分别来看两种调用方式的具体过程:
using namespace std;
_declspec(dllimport) int add(int a,int b);
{
???????? cout<<"3+5="<<add(3,5)<<endl;???????? return 1;
}
#include <windows.h>
using namespace std;
{
??????? ?HINSTANCE hInstance=LoadLibrary("DllTest.dll");
?????????typedef int (*AddProc)(int,int);
?????????AddProc Add=(AddProc)GetProcAddress(hInstance,?add@@YAHHH@Z);
?????????if(!Add)
?????????{
??????????????????cout<<"动态连接库库函数未找到"<<endl;
??????????????????return 0;
?????????}?????????cout<<"3+5="<<Add(3,5)<<endl;???????? FreeLibrary(hInstance);?????????return 1;
}??????? 以上代码并不复杂,首先定义一个实例句柄用来引用由Windows API?函数LoadLibrary加载的动态链接库,LoadLibrary函数的参数是一个字符串指针,具体调用时我们需要填入需要加载的动态链接库的位置及文件名,加载成功后返回一个实例句柄。接下来我们定义一个函数指针类型,用该类型声明一个函数指针,用来存储GetProcAddress函数返回的动态库函数入口地址。GetProcAddress能从指定的动态库中查找指定名字的函数,如果查找成功则返回该函数的入口地址,如果失败则返回NULL。更多GetProcAddress函数的用法请参看MSDN。有人可能注意到,GetProcAddress函数中指定的函数名并不是add,而是?add@@YAHHH@Z。这里就和前面将的函数调用方式联系起来了,在GetProcAddress函数中,我们指定的函数名必须是编译后经过重命名的函数名,而不是源文件中定义的函数名。这样实际上给我们的调用带来了相当大的麻烦,因为我们不可能去了解每一个经过重命名的导出函数名。好在微软已经给出了解决方法,那就是在编写动态链接库时同时编写一个以def为后缀的编译命名参考文件,如果动态链接库工程中有该文件,则编译器会根据该文件指定的函数名来导出动态库函数,关于def文件的详细使用方法请参考MSDN,这里就不一一赘述。找到需要的动态库函数后,我们就可以按需要对它进行调用,之后调用FreeLibrary函数释放动态库。因为动态库是多进程共享的,因此调用FreeLibrary函数并不意味着动态库在内存中被释放,每个动态库都有一个变量用来记录它的共享引用计数,而FreeLibrary的功能只是将这个记数减一,只有当一个动态库的引用计数为0时,它才会被操作系统释放。???????
