1、导出普通函数
为了让DLL导出一些函数,需要在每一个将要被导出的函数前添加标识符:_declspec(dllexport)如:
_declspec(dllexport) int add(int a,intb){return a+b;}
编译生成DLL后使用dumpbin查看的到
Ordinal hint RVA name
1 0 0001107D ?add@@YAHHH@Z = @ILT+120(?add@@YAHHH@Z)
其中Ordinal信息:1是导出函数的序号
Hint:数字是提示码,该信息不重要。
RVA:列出的地址是导出函数在DLL模块中的位置
Name:是导出函数的名称。?add@@YAHHH@Z主要是重载函数的命名。参见重载函数的命名方式。
2、导出C++类
实际上在动态链接库中还可以导出C++的类例如:
#define DLL_EXPORT_declspec(dllexport)
Class DLL_EXPORTPoint{
Public:
Void output(int x,int y);
}
也可以单独导出类的的成员函数
Class Point{
Public:
Void DLL_EXPORT output(int x,int y);
}
3、导出类的名称问题。
因为C++编译器会在编译的时候对名字进行改编。所以我们希望动态链接库文件在编译的时候,导出的函数的名称不要发生改变,为了实现这一目的,在定义导出函数时,需要加上限定符:extern”C”。双引号中的C一定要大写。该限定符会直接导出函数的名称而不加修改如add。
优点:利用限定符extern”C”可以解决C++和C语言之间相互调用时的函数命名问题。
缺点:不能用于导出一个类的成员还输,和重载函数。(C语言没有重载函数)。
4、不同语言之间的DLL调用
_cdecl是C和C++程序的缺省调用方式。但是在一些场合需要使用标准调用_stdcall。如delphi调用VC写的dll时要使用_stdcall的调用方式。但是使此时使用extern”C”将C++转变成C,但是标准调用会将名字改编。如add函数:_add@8,其中以下划线开始,add表示函数名,@8表示函数参数占的字节。
5、调用冲突的解决办法
为解决函数名的改变问题,可以在DLL工程下面添加一个模块定义文件(.def)来解决名字改编问题。Def的文件内容为
LIBRARY DLLTest
EXPORTS
add
output
其中DLLTest为动态链接库的名称add和output为所要导出的函数名称。
但此时仍然不能解决函数重载问题,函数重载。因为def只能对应单一的问题,如果要解决此问题还是要根据改变后的名字该重新设定
如: "int __stdcalladd(int,int)"改编后的名字为:(?add@@YGHHH@Z)
"float __stdcall add(float,float)" 改编后的名字为:(?add@@YGMMM@Z)
因此def文件应改写为:
LIBRARY DLLTest
EXPORTS
addInt =(?add@@YGHHH@Z)
addFloat =(?add@@YGMMM@Z)
def文件中的名字是唯一的,所以对应重载函数要写成不同的名字。
Def导出类比较麻烦,不建议使用def导出类,而是直接使用
Class declspec(dllexport)Point{
Void _stdcalladd(int x,int y);
};的形式导出。