添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
深沉的黑框眼镜  ·  python - ...·  2 月前    · 
威武的茴香  ·  执行命令./configure ...·  1 月前    · 
暗恋学妹的红金鱼  ·  jnidispatch ...·  6 小时前    · 
任性的包子  ·  江苏省政协·  6 月前    · 
温柔的跑步鞋  ·  长弓 燧龙 全 合集 ...·  10 月前    · 
安静的饭盒  ·  中山市统计局政务网站·  11 月前    · 
2048 ACM AUFS Android AppStore Aria2 Async BFS Bash BitTorrent BloomFilter Bytebase C++ CAP CDN CGLib动态代理 Cache Caffeine Cargo CentOS Cgroups Chrome扩展 Cli ClickHouse Cling Clion Colima Container DFS DLL Database Docker Docker-Compose Dubbo Easegress ElasticSearch Electron English Express FFmpeg FTP FaaS Future GC GRPC Gin Git Gitee Github Github-Actions Golang Gomod Goroutine GraalVM Gradle GraphQL HAProxy Hadoop IDEA配置 ID生成器 IO模型 Ingress JDK动态代理 JVM Java JavaWeb Java基础 Java注解 Java源码 Java面试 JuiceFS JupyterLab Kubernates Kubernetes LVS Lambda表达式 LevelDB Library Life Linux LruCache MacOS Maven MongoDB MySQL Mybatis NIO NPM Namespace Netty Newman Nginx Node.js Okular Panic PipelineDB PostgreSQL Postman Promise Protobuf Python RAII RPC RSS React Redash Redis Ruby Rust SPA SQLite SSE SSH Serverless Shadowsocks Skiplist Slice Socket Spring Spring Boot Spring源码 String Swagger TOML Telegraf Tomcat TypeScript Typora UUID UnionFS VMWare VPN WebAssembly Websocket XML YAML Zookeeper apt autok3s cURL chroot cmder grpc hash冲突 inoreader k3s kafka keepalived minikube picocli telnet vcpkg xorm 二分法 二叉树 二维码 交叉编译 代理模式 位运算 信号 信息聚合 内存管理 内网穿透 函数式接口 分布式 动态规划 协程池 博客管理 博客美化 双指针 反射 哈希表 回溯 回溯法 多线程下载 大数据 字符串 字符串处理 学习案例 容器 工具分享 并发编程 序列化 建筑 开发规范 开源协议 性能指标 技术杂谈 排序 搜索 操作系统 数学 数据库 数组 文字隐藏 断点续传 新浪微博 日志 日本語 日記 服务自建 查找 正则表达式 汇编语言 流式数据库 测试 滑窗 版本控制 生活总结 福利分享 程序人生 算法 算法题目 类加载器 素数 线性探测法 线程池 编译优化 编译器 网盘 网络线路 自启动 蒙特卡罗方法 虚拟机 街景 装饰器模式 计算机网络 设计模式 课程 负载均衡 贪心 软件安装与配置 软件推荐 软件测试 软考 进度条 进程通信 逆向工程 递归 重定向 重装系统 链表 队列 随机图片 集合 零宽字符 静态代理 面试总结 项目总结 项目构建 项目部署 黑苹果

cmake生成动态链接库dll

DLL概述

为了更好的实现代码复用,DLL库应运而生;与Windows平台下的exe格式相比,DLL可以被认为是一个单独的组件;

通过使用 DLL,可以将程序模块化为单独的组件。 例如,会计程序可以按模块出售;

如果安装了该模块,则每个模块都可以运行时加载到主程序中。 由于模块是分开的,因此程序的加载时间会更快。 并且仅在请求该功能时加载模块;

DLL 优势:

  • 使用更少的资源

    当多个程序使用相同的函数库时,DLL 可以减少在磁盘和物理内存中加载的代码的重复。 它不仅会大大影响前台运行的程序的性能,还会影响在 Windows 操作系统上运行的其他程序的性能。

  • 提升模块化体系结构

    DLL 有助于推动开发模块化程序。 它可以帮助你开发需要多种语言版本的大型程序或需要模块化体系结构的程序。 模块化程序的一个示例是一个会计程序,该程序具有许多可在运行时动态加载的模块。

  • 简化部署和安装

    当 DLL 中的函数需要更新或修复时,DLL 的部署和安装不需要程序与 DLL 重新链接。 此外,如果多个程序使用相同的 DLL,则多个程序都将从更新或修复中获益。 在使用定期更新或修复的第三方 DLL 时,此问题可能更频繁地出现。

    关于DLL的Microsoft官方文档:

  • https://docs.microsoft.com/zh-cn/troubleshoot/windows-client/deployment/dynamic-link-library
  • 其中,根目录下放置了 CMakeLists.txt ,用于声明整个项目;

    run_dll.cpp 为最终生成DLL库的加载测试代码,这里可以暂时忽略;

    lib 目录下就是生成DLL库的代码;

    在这个简单的项目中,我们会在C++代码中创建一个 add 函数,用于实现两个数字相加,并返回结果;

    生成DLL库,以供其他代码调用;

    编写C++生成DLL

    初始化项目

    首先在项目根目录创建 CMakeLists.txt ,并编辑:

    cmake_minimum_required(VERSION 3.16)
    project(dll_learn)
    set(CMAKE_CXX_STANDARD 20)
    # 声明引入子项目(目录)
    ADD_SUBDIRECTORY(lib)

    声明一些项目属性,创建lib目录,初始化整个项目;

    初始化lib库

    在lib目录下,我们会真正的编写DLL库;

    首先在lib目录下创建一个 CMakeLists.txt ,声明为一个子项目;

    # 设置变量
    SET(LIBHELLO_SRC ./my_dll.h ./my_dll.cpp)
    SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ../lib_out)
    # 第一个参数为你需要构建的dll的名字,第二个为类型
    ADD_LIBRARY(my_dll SHARED ${LIBHELLO_SRC})
    INSTALL(TARGETS my_dll)
    # 为dll设置linker
    # 指定dll的生成目录,这里是:./lib_out
    SET_TARGET_PROPERTIES(my_dll PROPERTIES LINKER_LANGUAGE C
            RUNTIME_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
            LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
            OUTPUT_NAME "my_dll"
            PREFIX "")

    首先使用 SET 定义了两个变量:

  • LIBHELLO_SRC :lib库源文件;
  • CMAKE_LIBRARY_OUTPUT_DIRECTORY :编译最终输出目录;
  • 随后使用 ADD_LIBRARY 声明了这是一个lib库;

    然后使用 INSTALL 指定了目标 my_dll ,并使用 SET_TARGET_PROPERTIES 设置了目标的属性:

  • PROPERTIES LINKER_LANGUAGE C :声明为C链接库,提高DLL库的兼容性;
  • XXX_OUTPUT_DIRECTORY :指定输出命令;
  • OUTPUT_NAME :DLL库输出名称;
  • PREFIX :DLL库前缀,若不设置,可能会加默认前缀;如, cygmy_dll.dll
  • 更多关于CMake的内容可以查询相关文档!

    编写lib库代码

    在lib目录下的CMakeLists中,我们声明了 my_dll.cpp my_dll.h 两个文件;

    下面我们来编写这两个文件;

    my_dll.h

    #ifndef CPP_LEARN_MY_DLL_H
    #define CPP_LEARN_MY_DLL_H
    #define EXPORT_DLL __declspec(dllexport)
    extern "C" EXPORT_DLL int add(int a, int b); // 即 int add(int a,int b)
    #endif //CPP_LEARN_MY_DLL_H

    my_dll.cpp

    #include "my_dll.h"
    int add(int a, int b) {
        return (a + b);
    

    在头文件中,我们使用__declspec(dllexport)声明并导出了一个dll函数;

    使用extern "C"也是为了导出为C代码,以提高代码兼容性;

    在cpp文件中,我们定义了头文件中声明的add函数;

    至此,dll库就已经编写完成了!

    编译DLL

    在根目录先使用cmake .命令,在根目录和lib目录下编译出Makefile文件;

    然后在根目录或者lib目录下使用make install即可编译出DLL库;

    编译出的DLL库位于根目录的lib_out目录下,名称为:my_dll.dll

    和我们在cmake中配置的完全相同;

    测试DLL

    为了测试生成的DLL能否正常运行,我们需要编写代码并加载测试生成的DLL是否可以正常被调用;

    在根目录下创建run_dll.cpp

    run_dll.cpp

    #include <windows.h>
    #include <iostream>
    typedef int (*add)(int, int);
    int main() {
        HINSTANCE handle = LoadLibrary("./lib_out/my_dll.dll");
        auto f = (add) GetProcAddress(handle, "add");
        std::cout << f(1, 32) << std::endl;
        FreeLibrary(handle);
        return 0;
    

    在run_dll.cpp中,我们首先动态加载了生成的DLL库,随后获取到了add方法,最后调用并输出了求和结果;

    编写完成后,还要在根目录中的CMakeLists.txt中添加编译可执行文件的声明:

    cmake_minimum_required(VERSION 3.16)
    project(dll_learn)
    set(CMAKE_CXX_STANDARD 20)
    ADD_SUBDIRECTORY(lib)
    + add_executable(run_dll run_dll.cpp)

    然后重新执行cmake .make install编译项目;

    这时会在项目根目录生成run_dll.exe

    运行run_dll.exe,可以生成结果:

    $ run_dll.exe
    

    测试成功!

    关于其他编程语言加载DLL库,见:

  • 各编程语言加载并调用dll库
  •