VS中动态库(外部库)导出与使用

在 C++ 中,动态库(Dynamic Link Library, DLL)允许模块化开发,运行时加载共享代码。以下是 动态库的导出与使用 的详细步骤:


1. 动态库(DLL)的导出

(1) 定义导出宏

在头文件中定义一个宏,用于控制 __declspec(dllexport)(编译 DLL 时)和 __declspec(dllimport)(使用 DLL 时):

cpp 复制代码
// MyDLL.h

#ifdef MYDLL_EXPORTS
    #define MYDLL_API __declspec(dllexport)  // 编译 DLL 时导出
#else
    #define MYDLL_API __declspec(dllimport)  // 使用 DLL 时导入
#endif

(2) 导出函数/类

在 DLL 项目中,使用 MYDLL_API 标记要导出的函数或类:

cpp 复制代码
// MyDLL.h
MYDLL_API int Add(int a, int b);  // 导出函数

class MYDLL_API MyClass {         // 导出类
public:
    MyClass();
    int Multiply(int a, int b);
};

(3) 实现 DLL 函数

cpp 复制代码
// MyDLL.cpp
#include "MyDLL.h"

int Add(int a, int b) {
    return a + b;
}

MyClass::MyClass() {}
int MyClass::Multiply(int a, int b) {
    return a * b;
}

(4) 编译生成 DLL

  • Visual Studio

    • 右键项目 -> 属性 -> 配置类型 设为 "动态库(.dll)"
    • 编译后生成 MyDLL.dllMyDLL.lib(导入库)。
  • CMake

    cmake 复制代码
    add_library(MyDLL SHARED MyDLL.cpp MyDLL.h)

2. 使用动态库

(1) 包含头文件

cpp 复制代码
#include "MyDLL.h"  // 确保头文件路径正确

(2) 链接导入库(.lib)

  • 方法 1:项目属性配置(推荐)

    • 右键项目 -> 属性 -> 链接器 -> 输入 -> 附加依赖项 ,添加 MyDLL.lib
    • 链接器 -> 常规 -> 附加库目录 ,添加 .lib 所在路径。
  • 方法 2:#pragma comment(仅 MSVC)

    cpp 复制代码
    #pragma comment(lib, "MyDLL.lib")

(3) 运行时加载 DLL

确保 MyDLL.dll 位于:

  • 可执行文件(.exe)所在目录,或
  • 系统 PATH 环境变量包含的路径。

3. 动态加载(运行时显式加载)

如果不想静态链接 .lib,可以使用 LoadLibrary + GetProcAddress 动态加载:

cpp 复制代码
#include <windows.h>

typedef int (*AddFunc)(int, int);  // 定义函数指针类型

int main() {
    HMODULE hDll = LoadLibrary(L"MyDLL.dll");
    if (!hDll) {
        std::cerr << "Failed to load DLL!" << std::endl;
        return 1;
    }

    // 获取函数指针
    AddFunc add = (AddFunc)GetProcAddress(hDll, "Add");
    if (!add) {
        std::cerr << "Failed to find function!" << std::endl;
        FreeLibrary(hDll);
        return 1;
    }

    int result = add(3, 4);  // 调用 DLL 函数
    std::cout << "Result: " << result << std::endl;

    FreeLibrary(hDll);  // 释放 DLL
    return 0;
}

4. 常见问题

(1) 无法解析的外部符号(LNK2019)

  • 原因 :未正确链接 .lib__declspec(dllimport) 未正确应用。
  • 解决
    • 检查 MYDLL_API 是否正确定义。
    • 确保 .lib 路径正确。

(2) 找不到 DLL(运行时错误)

  • 原因DLL 不在 PATH.exe 目录。
  • 解决
    • DLL 复制到 .exe 所在目录。
    • 或修改 PATH 环境变量。

(3) 名称修饰(Name Mangling)问题

  • C++ 函数名会被编译器修饰 (如 ?Add@@YAHHH@Z),导致 GetProcAddress 失败。
  • 解决
    • 使用 extern "C" 避免修饰:

      cpp 复制代码
      extern "C" MYDLL_API int Add(int a, int b);
    • 或使用 .def 文件显式导出名称。


5. 跨平台兼容(Linux/macOS)

在 Linux/macOS 上,动态库是 .so(Shared Object)或 .dylib,使用 __attribute__((visibility("default"))) 导出:

cpp 复制代码
// MyDLL.h
#ifdef __GNUC__
    #define MYDLL_API __attribute__((visibility("default")))
#else
    #define MYDLL_API __declspec(dllexport)
#endif

编译:

bash 复制代码
g++ -shared -fPIC -o libMyDLL.so MyDLL.cpp

使用:

cpp 复制代码
#include <dlfcn.h>
void* handle = dlopen("libMyDLL.so", RTLD_LAZY);
auto add = (AddFunc)dlsym(handle, "Add");

总结

步骤 Windows (DLL) Linux/macOS (SO)
导出宏 __declspec(dllexport) __attribute__((visibility("default")))
导入宏 __declspec(dllimport) (通常不需要)
编译 cl /LD MyDLL.cpp g++ -shared -fPIC -o libMyDLL.so MyDLL.cpp
加载方式 LoadLibrary + GetProcAddress dlopen + dlsym

正确导出和使用动态库可以大幅提高代码的模块化和复用性。

相关推荐
Mr Aokey1 小时前
手写Java线程池与定时器:彻底掌握多线程任务调度
java·开发语言
✿ ༺ ོIT技术༻2 小时前
笔试强训:Day2
开发语言·c++·笔记·算法
Jackson@ML3 小时前
如何快速高效学习Python?
开发语言·python
聂 可 以4 小时前
推荐几个可以免费下载视频的软件(Neat Download Manager、蜗牛下载助手、bilidown)
windows·开源软件
西瓜本瓜@5 小时前
在Android中如何使用Protobuf上传协议
android·java·开发语言·git·学习·android-studio
UFIT5 小时前
Python函数与模块笔记
开发语言·python
机智的人猿泰山5 小时前
java kafka
java·开发语言·kafka
Y1nhl5 小时前
搜广推校招面经八十一
开发语言·人工智能·pytorch·深度学习·机器学习·推荐算法·搜索算法
Algorithm15765 小时前
谈谈接口和抽象类有什么区别?
java·开发语言
Starry_hello world5 小时前
C++ 快速幂算法
c++·算法·有问必答