目录
DLL的两种调用方法
-
隐式调用:
#pragma comment(lib,"Dll.lib") //dll的名称 //也可以在项目属性中设置相应属性 //1. 在生成dll的项目中,属性->常规->输出目录:改为测试文件(.cpp)所在的目录 //2. 然后在测试程序中,vc++目录->包含目录,增加(dll头文件的名称).h文件所在的目录处 // 在链接器->输入->附加依赖项 (lib文件的名称).lib文件
在程序编译的时候就将lib编译到可执行文件中。它是通过通过PE的输入表在进入入口函数之前将
dll
加载到内存空间.
优点:调用方便,可以不用带着dll
文件。
不足:编译出来的程序很大
(静态链接库中不能再包含其他的动态链接库或者静态库) -
显示调用:
#include <Windows.h> //动态条用要使用到 Windoows 的 API。 //LoadLibrary //L:涵义待查 HINSTANCE hDLL = LoadLibrary(L"dll.dll"); //获得DLL的句柄,是DLL文件被映射到进程地址空间的虚拟地址。 if(hDLL){ //GetProcAddress typedef int(*p_fun)(int a, int b); p_fun addFunction = (p_fun)GetProcAddress(hDLL, "add"); //得到函数的地址 } //调用函数 addFunction(1+2); //FreeLibrary FreeLibrary(hDll);
在程序运行过程中,需要用到
dll
里的函数时,再动态加载dll
到内存中.
优点:让程序启动更快,而且dll
的维护更容易,使得程序如果需要更新,很多时候直接更新dll
即可。
不足:函数调用稍微复杂一点,调用需要带DLL
(动态链接库中还可以再包含其他的动态或静态链接库。)
extern “C” 详解
extern关键字解释
extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。静态库之间的调用用extern来进行相互调用,注意它只是声明了变量的类型,并没有定义变量(变量未分配地址)。
修饰名
C + + 函数的修饰名包含以下信息:
-
函数名称。
-
函数所属的类(如果该函数为成员函数)。 这可能包括含有函数所属的类的类,等等。
-
函数所属的命名空间(如果该函数是命名空间的一部分)。
-
函数参数的类型。
-
调用约定。
-
函数的返回类型。
未修饰名 修饰名 int a(char){int i=3;return i;};
?a@@YAHD@Z
void __stdcall b::c(float){};
?c@b@@AAGXM@Z
C 修饰名的格式:
C 函数的修饰形式取决于其声明中使用的调用约定,如下表所示。 这也是在将 C++ 代码声明为具有 extern "C"
链接时使用的修饰格式,默认调用约定是 __cdecl
。 请注意,在 64 位环境中,不修饰函数。
__cdecl |
前导下划线( _ ) |
---|---|
__stdcall |
前导下划线( _ )和尾随 at 符号( @ ),后接参数列表中的字节数(以十进制为单位) |
__fastcall |
前导和尾随 at 符号( @ ),后跟一个表示参数列表中的字节数的十进制数 |
__vectorcall |
两个尾随 at 符号( @@ ),后接参数列表中的十进制字节数 |
(由此可知为什么GetProcAddress
的函数名书写很麻烦,所以要注意函数名(修饰名)的问题,但是c的修饰名的书写相对简单,因此使用extern “C”,便于我们编写对dll
调用)
extern “C”解释
extern “C” 修饰的变量和函数表示:按照C语言方式编译和链接