动态库导出
在 Windows 平台下,导出dll/lib 一般会指定/DEF, 即导出函数的名称。下面的代码简化了导出函数的形式:
#ifdef MM_EXPORTS
#define MMAPI __declspec(dllexport)
#else
#define MMAPI __declspec(dllimport)
#endif
如果使用了上边的代码,就可以省略定义 DEF 文件。更多细节参见: https://docs.microsoft.com/en-us/cpp/build/exporting-from-a-dll-using-declspec-dllexport?view=vs-2019
与其正交的一个概念是函数调用约定形式。即 __cdecl
和 __stdcall
。 这和 __declspec(dllexport)
是不想关的。一般C/C++ 的导出函数调用约定为__cdecl
。 关于 调用约定参见 __cdecl VS __stdcall
简而言之:
- __cdecl 是C/C++ 默认调用形式。 VS 编译器选项:/Gd
int __cdecl sumExample(int a, int b);
调用约定__cdecl特征是:
参数从右开始传递,并放置在堆栈上。
堆栈清理由调用方执行。
函数名称通过用下划线字符"_"进行前缀来修饰。
2. __stdcall 标准调用通常见于WIN32函数,其实就是 WINAPI__stdcall. VS编译器选项: /Gz
#define WINAPI __stdcall
声明一个 标准调用的函数:
int __stdcall sumExample(int a, int b);
__stdcall调用约定的主要特征是:
参数传递顺序从右至左,并放置在堆栈上。
堆栈清理由被调用方执行。
函数名称通过前缀下划线字符并追加"@"字符和所需的堆栈空间字节数来修饰。
3. __fastcall 略
4. thiscall C++ 类内调用
总结下:
__cdecl是 C/C++的默认调用约定。此调用的优点是,它允许使用具有可变参数数的函数。缺点是它创建更大的可执行文件。
__stdcall用于调用 Win32 API 函数。它不允许函数具有可变数量的参数。
一个示例:
Header
#ifndef DPS_H
#define DPS_H
#ifdef __cplusplus
extern "C" {
#endif
#if (defined _WIN32) || (defined _WINDOWS) || (defined WIN32)
#ifdef MM_EXPORTS
#define MMAPIEXP __declspec(dllexport)
#else
#define MMAPIEXP __declspec(dllimport)
#endif
#define MMAPI __cdecl
#else
#define MMAPIEXP
#define MMAPI
#endif
// 一个简单的回调
typedef int(MMAPI *MMCallBack)(int handle, char* UserData);
typedef struct
{
int a;
int b;
char c;
char* d;
}DataInfo;
typedef struct
{
int (MMAPI* func01)(int a, int b);
void (MMAPI* func02)(int c, DataInfo* d, MMCallBack cb);
}MMAPIINFO;
// 要导出的函数
MMAPIEXP void MMAPI GetAPI(MMAPIINFO* api);
#ifdef __cplusplus
}
#endif
#endif
CPP 文件
#include "dps.h"
int func01(int a, int b){
return a + b;
}
void func02(int c, DataInfo* d, MMCallBack cb){
cb(c,d->d);
}
void GetAPI(MMAPIINFO* api){
MMAPIINFO* ret = new MMAPIINFO();
ret->func01 = func01;
ret->func02 = func02;
}