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

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

相关推荐
我不会编程55516 小时前
Python Cookbook-5.1 对字典排序
开发语言·数据结构·python
李少兄16 小时前
Unirest:优雅的Java HTTP客户端库
java·开发语言·http
懒羊羊大王&17 小时前
模版进阶(沉淀中)
c++
无名之逆17 小时前
Rust 开发提效神器:lombok-macros 宏库
服务器·开发语言·前端·数据库·后端·python·rust
似水এ᭄往昔17 小时前
【C语言】文件操作
c语言·开发语言
啊喜拔牙17 小时前
1. hadoop 集群的常用命令
java·大数据·开发语言·python·scala
owde17 小时前
顺序容器 -list双向链表
数据结构·c++·链表·list
xixixin_17 小时前
为什么 js 对象中引用本地图片需要写 require 或 import
开发语言·前端·javascript
GalaxyPokemon17 小时前
Muduo网络库实现 [九] - EventLoopThread模块
linux·服务器·c++
W_chuanqi17 小时前
安装 Microsoft Visual C++ Build Tools
开发语言·c++·microsoft