1.导出函数的方法:
(1)在要导出的函数签名(signature)上添加关键字__declspec(dllexport)
例如:
void __declspec(dllexport) _cdecl someFun() { printf("Hello, World!\n"); }
使用这种方法导出的函数,函数的名称修饰,为默认的修饰方法:
(1).1 若是从.c文件中导出则使用c语言的名称修饰规则,其规则与函数的调用约定相关:
① _cdecl/__cdecl(c调用约定):要导出的函数名称是someFun,名称修饰后为:someFun,也即是说,在该调用约定下,原函数名称和修饰名称是相同的不发生任何变化,包括大小写!
② _stdcall/__stdcall(标准调用约定):要导出的函数名称是someFun,名称修饰后为:_someFun@0,即其名称修饰规则为:在原函数名称前面加一个下划线(_),在原函数名称后面加一个at符号(@),最后跟上该函数声明的形式参数的类型所占的字节数的总和。
③ _fastcall/__fastcall(寄存器传参约定):要导出的函数名称是someFun,名称修饰后为:@someFun@0,即其名称修饰规则为:在原函数名称的前面和后面各加一个at符号(@),最后跟上该函数声明的形式参数的类型所占的字节数的总和。
(1).2 若是从.cpp文件中导出则使用c++语言的名称修饰规则,其规则与函数的调用约定相关:其名称修饰与函数的调用约定,函数参数类型,函数返回值类型,函数的作用域(全局函数,类的成员函数)等有关,具体参考MSDN。
提示:C++全局函数名称修饰的快速识别方法:
① _cdecl/__cdecl: ?functionname@@YA......
② _stdcall/__stdcall: ?functionname@@YG......
③ _fastcall/__stdcall: ?functionname@@YI......
(1).3 编译方法:
① cl /LD filename.c[pp] ------>filename.lib and filename.dll
② cl /c filename.c[pp] --->filename.obj
link /dll filename.obj ---->filename.lib and filename.dll
(2)使用.def文件的方式导出函数
函数的签名(signature)正常编写,不需要加__declspec(dllexport),
例如:
void _cdecl someFun()
{
printf("Hello, World!\n");
}
编写好源文件后,还要编写一个 .def 文件,其编写方法如下:
LIBRARY Fun3
EXPORTS
SomeFun1 = somefun1
SomeFun2 = somefun2@0
SomeFun3 = @somefun3@0
将以上内容保存成一个.def文件,例如 mydll.def ,其中的 Fun3 是dll文件中的文件名,EXPORTS语句可以为导出函数起别名,其方法如上所示:别名 = 函数的修饰名称(也叫内部名称),这种方法为可以改名,从而使用DLL可以被其他语言编写的程序使用(符号一致).
编译方法:
① cl /LD filename.c[pp] /link /DEF:mydll.def
② cl /c filename.c[pp]
link /DEF:mydll.def filename.obj ------>filename.dll and filename.lib
2. 使用从DLL文件中导出的函数的方法:
① 如果生成DLL文件时有函数签名的头文件( .h 文件),则将其 include 到我们的源文件中来(#include "filename.h"),这时就可以在源文件中直接使用.h中的导出函数名称,在程序编译好后,连接时要连接上dll对应的lib文件!!
② 没有头文件:使用以下声明:
void __declspec(dllimport) _cdecl funcationname();
注意:函数的签名要与函数的导出时的签名完全一致,包括调用约定,static,const修饰等,同时还有加上__declspec(dllimport)关键字,在程序编译好后,连接时要连接上dll对应的lib文件!!
③ 使用LoadLIbrary动态加载
3.关于extern "C"
因为c语言与c++语言的Name Decoration规则不同,为了使C语言编写的DLL在c++程序中能使用,则需要在导出函数时加上extern "C",表明使用C的名称修饰,这样才能,正确找到符号,正确调用函数!
例如:
//filename.cpp
extern "C" void __declspec(dllexport) __cdecl somefun1()
{
printf("Hello, World!\n");
}
注意:使用该函数时其,声明为void __declspec(dllimport) __cdecl somefun1();
不需要也不能加 extern "C" !!!!
4. PE中的函数符号问题
PE文件中的导入表中存放有该PE文件使用的所有外部符号(从其他DLL文件中导出的函数符号),就是经过名称修饰的符号,具体符号形式由1中内容可知,DLL文件中的导出表中同样存放有该DLL的导出的函数的符号,DLL加载器,就是根据这些符号来定位函数的信息(函数名称,函数的虚拟地址)然后进行重定位。
5.COM(Component Object Module)中的 Create 型函数常常声明为extern "C"和__cdecl,这样保证了函数在编译成二进制文件时其名称修饰后的函数名称仍为原函数名。实现了COM的二进制级别的语言无关性和实现与接口分离的特性!!
用dll来导出类,实际上就是导出类的方法而已.(使用名字修饰)
6.dll中导出数据
6.1到出普通数据
在dll中导出时,用__declspec(dllexport)修饰待导出的变量,在使用时使用__declspec(dllimport)来修饰该变量,例如:
//some.dll
__declspec(dllexport) int a = 0;
在客户端使用时:要将生成some.dll时产生的导入库文件soem.lib链接到客户端程序中
__declspec(dllimport) int a;
6.2到导出共享数据(在dll中创建一个共享段)
#pragma data_seg(".shared")
__declspec(dllexport) int a = 0;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.shared, RWS")
使用代码在dll中建立了一个共享段,同时在该段中导出了一个共享变量a
客户端可以使用__declspec(dllimport) int a;来使用该变量。
注意:在创建一个具有RWS属性的段时一定要对变量进行初始化,否则,创建该段会失败,编译通不过。