C++动态调用动态链接库(DLL或SO)主要步骤:
动态库调用流程大致可以描述为:0.创建动态库 -> 1.加载动态库 -> 2.定义函数类型 -> 3.获取函数地址 -> 4.调用函数 -> 5.卸载动态库。
这个流程和逻辑可以在不同的操作系统和编译器下略有差异,因此需要根据特定的平台和工具链做适当的调整。
0.创建动态库:
创建一个接口,并生成可供其他程序使用的DLL和SO(动态链接库),步骤如下:
要创建一个接口,并生成可供其他程序使用的DLL和SO(动态链接库),可以按照以下步骤进行:
- 创建接口头文件(例如
helloFunc.h
):在头文件中定义接口的函数和数据结构。将接口的所有公共部分放在这个头文件中,并确保使用适当的导出声明。
// helloFunc.h
#ifdef _MSC_VER // Windows环境下的导出声明
#ifdef helloFunc_EXPORTS
#define HELLOFUNC_API __declspec(dllexport)
#else
#define HELLOFUNC_API __declspec(dllimport)
#endif
#else // Linux/Unix下的导出声明
#ifdef HELLOFUNC_EXPORTS
#define HELLOFUNC_API __attribute__((visibility("default")))
#else
#define HELLOFUNC_API
#endif
#endif
// 接口函数
#ifdef __cplusplus
extern "C" {
#endif
HELLOFUNC_API void hello();
#ifdef __cplusplus
}
#endif
- 实现接口函数的源文件(例如
helloFunc.cpp
):在源文件中实现接口函数。确保使用正确的导出声明,并根据需要处理接口的具体逻辑。
// helloFunc.cpp
#include "helloFunc.h"
void hello() {
// 实现函数逻辑
// ...
}
- 生成 DLL 和 SO:
- 在Windows环境下,可以使用 Visual Studio 或 MinGW 等工具来生成 DLL 文件。将接口头文件和实现源文件添加到工程中,并设置导出选项,编译工程以生成 DLL 文件。
- 在Linux/Unix环境下,可以使用 GCC 或 Clang 等编译器来生成 SO 文件。将接口头文件和实现源文件编译成目标文件,然后使用编译器的特定选项和命令来将目标文件链接成 SO 文件。
1. 加载动态库:
使用操作系统提供的函数(如LoadLibrary()
、dlopen()
)加载动态库文件。需要指定动态库的文件路径或名称。
#if defined (WIN32) | defined (WIN64)
HMODULE handle = nullptr; // 动态库句柄
#else
void* handle = nullptr; // 动态库句柄
#endif
// 加载动态库
#if defined (WIN32) | defined (WIN64)
handle = LoadLibrary("example.dll"); // 在Windows中使用
#else
handle = dlopen("libexample.so", RTLD_LAZY); // 在Linux/Unix中使用
#endif
if (!handle) {
#if defined (WIN32) | defined (WIN64)
std::cerr << "无法加载动态库: " << GetLastError() << std::endl; // 在Windows中使用
#else
std::cerr << "无法加载动态库: " << dlerror() << std::endl;
#endif
return 1;
}
2. 函数类型定义:
使用函数指针来定义函数的类型,以便在动态库中找到的函数能够正确地调用。函数指针的类型必须与函数的签名(参数类型和返回类型)匹配。
void (*helloFunc)(); // 函数指针
3. 获取函数地址:
通过使用操作系统提供的函数(如GetProcAddress()
、dlsym()
)获取特定函数的地址。需要指定要调用的函数的名称。
// 获取函数地址
#if defined (WIN32) | defined (WIN64)
helloFunc = (void (*)())GetProcAddress(handle, "hello"); // 在Windows中使用
#else
helloFunc = (void (*)())dlsym(handle, "hello"); // 在Linux/Unix中使用
#endif
if (helloFunc == nullptr) {
#if defined (WIN32) | defined (WIN64)
std::cerr << "无法找到函数: " << GetLastError() << std::endl; // 在Windows中使用
FreeLibrary(handle); // 在Windows中使用
#else
std::cerr << "无法找到函数: " << dlerror() << std::endl;
dlclose(handle); // 在Linux/Unix中使用
#endif
return 1;
}
4. 调用函数:
通过调用函数指针,实现对动态库中函数的调用。根据函数的参数类型和返回类型,在适当的位置传递参数,并根据需要处理返回值。
// 调用函数
helloFunc();
5. 卸载动态库:
使用操作系统提供的函数(如FreeLibrary()
、dlclose()
)卸载已加载的动态库。通常在不再需要动态库时执行这个步骤。
// 卸载动态库
#if defined (WIN32) | defined (WIN64)
FreeLibrary(handle); // 在Windows中使用
#else
dlclose(handle); // 在Linux/Unix中使用
#endif
6.总结
- helloFunc.h
// helloFunc.h
#ifdef _MSC_VER // Windows环境下的导出声明
#ifdef helloFunc_EXPORTS
#define HELLOFUNC_API __declspec(dllexport)
#else
#define HELLOFUNC_API __declspec(dllimport)
#endif
#else // Linux/Unix下的导出声明
#ifdef HELLOFUNC_EXPORTS
#define HELLOFUNC_API __attribute__((visibility("default")))
#else
#define HELLOFUNC_API
#endif
#endif
// 接口函数
#ifdef __cplusplus
extern "C" {
#endif
HELLOFUNC_API void hello();
#ifdef __cplusplus
}
#endif
- helloFunc.cpp
// helloFunc.cpp
#include "helloFunc.h"
void hello() {
// 实现函数逻辑
// ...
}
- main.cpp
#include <iostream>
#if defined (WIN32) | defined (WIN64)
#include <windows.h> // 在Windows中使用
#else
#include <dlfcn.h> // 在Linux/Unix中使用
#endif
int main() {
#if defined (WIN32) | defined (WIN64)
HMODULE handle = nullptr; // 动态库句柄
#else
void* handle = nullptr; // 动态库句柄
#endif
// 1. 加载动态库
#if defined (WIN32) | defined (WIN64)
handle = LoadLibrary("example.dll"); // 在Windows中使用
#else
handle = dlopen("libexample.so", RTLD_LAZY); // 在Linux/Unix中使用
#endif
if (!handle) {
#if defined (WIN32) | defined (WIN64)
std::cerr << "无法加载动态库: " << GetLastError() << std::endl; // 在Windows中使用
#else
std::cerr << "无法加载动态库: " << dlerror() << std::endl;
#endif
return 1;
}
// 2. 函数类型定义
void (*helloFunc)(); // 函数指针
// 3. 获取函数地址
#if defined (WIN32) | defined (WIN64)
helloFunc = (void (*)())GetProcAddress(handle, "hello"); // 在Windows中使用
#else
helloFunc = (void (*)())dlsym(handle, "hello"); // 在Linux/Unix中使用
#endif
if (helloFunc == nullptr) {
#if defined (WIN32) | defined (WIN64)
std::cerr << "无法找到函数: " << GetLastError() << std::endl; // 在Windows中使用
FreeLibrary(handle); // 在Windows中使用
#else
std::cerr << "无法找到函数: " << dlerror() << std::endl;
dlclose(handle); // 在Linux/Unix中使用
#endif
return 1;
}
// 4. 调用函数
helloFunc();
// 5. 卸载动态库
#if defined (WIN32) | defined (WIN64)
FreeLibrary(handle); // 在Windows中使用
#else
dlclose(handle); // 在Linux/Unix中使用
#endif
return 0;
}