- 动态库函数调用的几种形式
- 动态库的关键字
- 生成动态库
动态库函数调用方式
_stdcall调用
_stdcall是Pascal程序的缺省调用方式,参数采用从右到左的压栈方式,由调用者完成压栈操作,被调函数自身在返回前清空堆栈。_cdecl调用
_cdecl是C/C++的缺省调用方式,参数采用从右到左的压栈方式,由调用者完成压栈操作 ,传送参数的内存栈由调用者维护。 _cedcl约定的函数只能被C/C++调用,每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。- 区别
两者最大的区别在于,函数调用完成后清理工作不一样。 前者是被调用函数自身清理,后者则是调用者清理,常用的Windows API函数均采用_stdcall方式进行调用,但是在特殊情况下我们需要第二种方式,当调用函数的参数是变化时,我们需要使用_cdecl方式进行调用。因为被调用函数自身无法确定自己的参数个数,从而无法清理堆栈。
但是在VS2010编译器中默认采用的是_cdecl方式进行编译的。
动态库关键字
C++ 中动态链接库–导入和导出的实例详解
__declspec(dllexport) 和 __declspec(dllimport):
__declspec(dllexport):编译器看到一个变量、函数或者C++类被它修饰,那么它就知道应该在生成的DLL 模块中导出该变量、函数或C++类。
__declspec(dllimport):编译器看到一个变量、函数或者C++类被它修饰,那么它就知道可执行文件或DLL的源文件需要从其它DLL模块中导入一些变量和函数。
DLL的导入段:
构建可执行模块时,可执行模块还可以包含一个导入段,其中列出了所有它需要的DLL模块的名称,对列出的每个DLL,该段还记录了可执行文件的二进制代码中引用的函数和变量的符号名。DLL中也有同样的导入段,记录它所需要的模块名称和函数、符号名。
DLL文件导出段:
链接器生成DLL的文件中会嵌入一个导出符号表,这个导出段列出了导出的变量、函数和类的符号名。可以使用相关导出工具查看,如dllexp。
extern “C”作用:
extern “C” 用来告诉编译器不要对变量名或函数名进行改编,这样C/C++或任何编程语言编写的可执行模块都可以访问该变量或函数。
使用.def文件生成兼容DLL
这样就不需要在要导出的东西(数据,函数,类,类成员)前些__declspec(dllexport)关键字了。
如果使用VS,IDE。可以在项目-属性-链接器-输入中修改该项目使用的DEF文件。
为项目创建一个.def文件;
LIBRARY “dlltest.dll”
EXPORTS
func-name
该种兼容方式,既能兼容适用C语言,又能使用C++。推荐使用这种方式。
生成动态库
- 三个文件
- VS配置问题
新建一个win32程序,选择动态库dll。
//test.h
#ifndef TEST_H
#define TEST_H
//定义导入和导出,根据需要定义相关宏,若作为导出库则定义DLL_IMPLEMENT否则不需要。
#ifdef DLL_IMPLEMENT
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif
#ifdef __cplusplus
extern "C" {
#endif
DLL_API int __stdcall Add(int a, int b);
#ifdef __cplusplus
}
#endif
#endif
//tets.cpp
#define DLL_IMPLEMENT
#include "test.h"
//你需要的头文件
#ifdef __cplusplus
extern "C" {
#endif
//显示说明函数调用方式为__stdcall方式,默认为__cdecl方式。项目属性--》c/c++--》高级--》调用约定修改
int __stdcall Add(int a, int b)
{
return (a+b);
}
#ifdef __cplusplus
}
#endif
//新建资源文件def定义导出函数Add。
//test.def
LIBRARY
EXPORTS
Add