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

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

相关推荐
蓝婷儿19 分钟前
6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础
开发语言·python·学习
渣渣盟35 分钟前
基于Scala实现Flink的三种基本时间窗口操作
开发语言·flink·scala
糯米导航1 小时前
Java毕业设计:办公自动化系统的设计与实现
java·开发语言·课程设计
糯米导航1 小时前
Java毕业设计:WML信息查询与后端信息发布系统开发
java·开发语言·课程设计
南岩亦凛汀1 小时前
在Linux下使用wxWidgets进行跨平台GUI开发
c++·跨平台·gui·开源框架·工程实战教程
MessiGo1 小时前
Javascript 编程基础(5)面向对象 | 5.1、构造函数实例化对象
开发语言·javascript·原型模式
大霞上仙2 小时前
nonlocal 与global关键字
开发语言·python
曦月逸霜2 小时前
第34次CCF-CSP认证真题解析(目标300分做法)
数据结构·c++·算法
galaxy_strive2 小时前
绘制饼图详细过程
开发语言·c++·qt
黑客老李2 小时前
JavaSec | SpringAOP 链学习分析
java·运维·服务器·开发语言·学习·apache·memcached