基于VS2019的C++动态链接库DLL生成与调用

一、理论知识及实践经验

实验注意事项及部分程序编写规范(部分源自ChatGPT-3.5)

 __declspec(dllexport)和__declspec(dllimport)是用于在C++中定义动态链接库(DLL)的关键字。在编写动态链接库时,__declspec(dllexport)用于指示编译器将函数或变量导出到DLL中,以便其他程序可以使用它们(如此编译生成.dll时,导出关键字宏标记的函数或变量就会被标记为可供其它程序调用的符号)。而__declspec(dllimport)则用于告诉编译器在使用DLL中的函数或变量时,需要从DLL中导入它们(温馨提示:①“__”是两个下划线;②一些平台特定细节,导出的函数需要使用extern ”C“指定编译和链接规约);

 #pragma once是C++中的预处理器指令,用于确保头文件只被编译一次。当编译器遇到#pragma once指令时,它会检查该头文件是否已经被包含,如果是,则跳过该文件的处理,否则将其包含在编译过程中。这种方法与传统的使用#ifndef和#define宏来防止头文件被多次包含的方法相比,更加简洁和直观。使用#pragma once可以减少编译时间并避免由于多次包含同一头文件而引起的重定义错误

 #pragma comment(lib, "×××.lib") 是一个特殊的预处理器指令,用于告诉编译器在链接时将特定的库文件包含到可执行文件中。这个指令告诉编译器在链接时自动包含名为"×××.lib"的库文件,以简化编译过程,特别是对于需要链接静态库的情况。但是,该种方法是特定于编译器的,不是标准的C++语法,在不同的编译器中可能会有不同的行为。

整个程序中的预处理变量包括头文件保护符必须唯一,通常的做法是基于头文件中类的名字来构建保护符的名字,以确保其唯一性。为了避免与程序中的其它实体发生名字冲突,一般把预处理变量的名字全部大写[1];

在C++中,typedef关键字用于为现有的数据类型创建一个新的名称。这样做有助于提高代码的可读性和可维护性。例如,typedef int *ptr意味着指向int数据类型的指针别名为ptr;typedef int (*ptr)(int,int)意味着指向参数为两个int类型数据和返回值为int类型的函数的函数指针别名为ptr;

.dll生成配置环境需要与其调用环境相一致(如Debug/Release版本、×64/×86);

×××_EXPORTS(×××一般为大写项目名称)通常会在创建动态链接库DLL的项目中定义。在Visual Studio中,当你创建一个新的DLL项目时,通常会自动生成一个预定义的宏,名称类似于项目名称加上“_EXPORTS”。这样就可以根据这个宏的定义来选择是导出还是导入函数和变量;

命名域的使用可以有效避免部分不同文件同名函数冲突,格式为namespace{函数}。

编程拓展

        确保头文件多次包含仍能安全工作的常用技术是预处理器(preprocessor),它由C++语言从C语言继承而来。预处理器是在编译之前执行的一段程序,可以部分地改变我们所写的程序[1]。

       预处理器指令可以在编译前对源代码进行处理,用于控制编译过程和生成特定的代码结构,预处理器指令通常以#开头。 C++中的预处理器指令包括#include(用于包含其他文件的内容)、#define(用于定义宏/预处理变量)、#ifdef / #ifndef / #else / #endif(头文件保护符,用于条件编译)、#pragma(用于向编译器发出特定的命令)、#error(用于在预处理阶段生成一个错误消息)、#warning(用于在预处理阶段生成一个警告消息)、#undef(用于取消已定义的宏)以及#line(用于修改行号和文件名信息)。

        预处理变量有两种状态:已定义和未定义。#define指令把一个名字设定为预处理变量,#ifdef(当且仅当变量已定义时为真) / #ifndef(当且仅当变量未定义时为真)两个指令分别检查某个指定的预处理变量是否已经定义。一旦检查结果为真,则执行后续操作直至遇到#endif指令为止[1]。常见组合代码行如下(注意,如果是多对#ifndef / #endif为就近配对):

                                        #ifndef ×××_H           (×××通常为编写类类名)

                                        #define ×××_H

                                        ....                             (此处可插入包含库文件、类的声明)

                                        #endif


        宏(Macro),在计算机编程中是指定如何将某个输入映射到替换输出的规则或模式。将宏应用于输入称为宏扩展。输入和输出可以是词汇标记或字符序列或语法树。软件应用程序支持字符宏,可以轻松调用常用命令序列。某些编程语言支持令牌和树宏,以实现代码重用或扩展语言,有时适用于特定领域的语言。宏用于使程序员可以将一系列计算指令作为单个程序语句使用,从而使编程任务不那么繁琐且不易出错。宏通常允许位置或关键字参数来指示条件汇编程序的内容和根据操作系统、平台或其他因素等变量生成并用于创建整个程序或程序套件[3]。

        在Windows模块开发中,宏经常和预处理指令#ifdef配合来控制模块的导出和导入符号;在跨平台开发时,通过宏和预处理指令配合,可以达到一定的代码开关控制,对不同的操作系统启用不同的代码;宏定义可以防止重复包含头文件;宏可以减少重复代码;宏可以简化部分信息输出[2]。  

二、实验实践

实验操作系统:Windows 10

实验高级程序设计语言:C++

实验开发环境:Visual Studio 2019

(一)C++源程序生成.dll动态链接文件及其使用[4][5][6][7]

1、编写生成.dll动态链接库

方法一

        方法一基于动态链接库(DLL)(项目模板)实现。需要注意的是,该模板自动提供预处理器定义CALCULATORAPIDLL_EXPORTS,而我们的项目名称为Calculator_API_DLL,实际上该处DLL导入导出宏需要匹配一致。

对应文件代码

// Calculator.h文件代码// 预防重复包含头文件
#ifndef CALCULATOR_H
#define CALCULATOR_H// Visual Studio创建DLL文件会自动添加Calculator_API_EXPORTS
// 通过宏与预处理命令控制DLL文件导入导出符号
#ifdef CALCULATORAPIDLL_EXPORTS
#define CalDLL_API __declspec(dllexport)
#else
#define CalDLL_API __declspec(dllimport)
#endif// 包含标准库
#include<iostream>// 添加结构体定义或函数定义
struct CalDLL_API CalNum
{int CalAdd(int, int);int CalSub(int, int);int CalPro(int, int);int CalDiv(int, int);
};namespace CalculatorSpace
{void CalDLL_API printHello();
}#endif //CALCULATOR_H// Calculator.cpp文件代码// 添加预编译头
#include "pch.h"#include"Calculator.h"using namespace std;inline int CalNum::CalAdd(int a, int b)
{return a + b;
}inline int CalNum::CalSub(int a, int b)
{return a - b;
}inline int CalNum::CalPro(int a, int b)
{return a * b;
}inline int CalNum::CalDiv(int a, int b)
{// 判断分母是否为除数if (b == 0){cout << "分母不能为零!计算失败!" << endl;return 0;}elsereturn a / b;
}void CalculatorSpace::printHello()
{cout << "hello World!" << endl;
}

方法二

        方法二基于空项目(项目模板)实现。需要注意的是,该模板由于不提供预处理器定义,因此,我们可以i将DLL导入导出宏命名为”项目名称_EXPORTS“。

对应文件代码

// Calculator.h文件代码// 预防重复包含头文件
#ifndef CALCULATOR_H
#define CALCULATOR_H// 通过宏与预处理命令控制DLL文件导入导出符号
#ifdef Calculator_API_EXPORTS
#define CalDLL_API __declspec(dllexport)
#else
#define CalDLL_API __declspec(dllimport)
#endif// 包含标准库
#include<iostream>// 添加结构体定义或函数定义
struct CalDLL_API CalNum
{int CalAdd(int, int);int CalSub(int, int);int CalPro(int, int);int CalDiv(int, int);
};namespace CalculatorSpace
{void CalDLL_API printHello();
}#endif //CALCULATOR_H// Calculator.cpp文件代码#include"Calculator.h"using namespace std;inline int CalNum::CalAdd(int a, int b)
{return a + b;
}inline int CalNum::CalSub(int a, int b)
{return a - b;
}inline int CalNum::CalPro(int a, int b)
{return a * b;
}inline int CalNum::CalDiv(int a, int b)
{// 判断分母是否为除数if (b == 0){cout << "分母不能为零!计算失败!" << endl;return 0;}elsereturn a / b;
}void CalculatorSpace::printHello()
{cout << "hello World!" << endl;
}

2、主程序调用.dll动态链接库

实验代码

#include<iostream>
#include"Calculator.h"using namespace std;int main()
{int a, b;cout << "请输入需要进行计算的数字a与b:" << endl;cin >> a >> b;// 测试类的调用// 调用结构体默认构造函数CalNum Caculator;int A1 = Caculator.CalAdd(a, b);int A2 = Caculator.CalAdd(a, b);int A3 = Caculator.CalAdd(a, b);int A4 = Caculator.CalAdd(a, b);cout << "a+b=" << A1 << endl;cout << "a-b=" << A2 << endl;cout << "a*b=" << A3 << endl;cout << "a/b=" << A4 << endl;// 测试函数调用CalculatorSpace::printHello();return 0;
};

实验结果

参考资料:

[1] C++ Primer中文版:第5版 /(美)李普曼(Lippman,S.B.),(美)拉乔伊(Lajoie,J.),(美)默(Moo,B.E.)著;王刚,杨巨峰译. —北京:电子工业出版社,2013.9.

[2] C/C++宏的基本使用方法附例子讲解_c++宏machine-CSDN博客

[3] https://en.wikipedia.org/wiki/Macro_(computer_science)

[4] VS2019编译生成动态链接库dll的两种方式_vs 编译dll-CSDN博客

[5] 手把手教你如何制作和使用lib和dll_user_plc. lib怎么编写-CSDN博客

[6] VS2019-C++创建和调用DLL动态链接库(傻瓜式教程)_创建动态链接库-CSDN博客

[7] https://www.cnblogs.com/gongxijun/p/4368404.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/594427.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AIGC年度回顾!2024向量数据库是否还是AI发展方向之一?

引言 2023 年&#xff0c;是 AI 技术大爆发的一年&#xff0c;从年初到年末&#xff0c;全球关心技术发展的人们见证了一次次的 AI 技术升级&#xff0c;也逐步加深着对 AGI 发展的畅想。而伴随着生成式人工智能的飞速发展&#xff0c;向量数据库以其独特的技术优势逐渐崭露头角…

修复移动硬盘显示盘符但打不开问题

问题&#xff1a; 移动硬盘显示盘符&#xff0c;但无法打开。点击属性不显示磁盘使用信息。 分析解决&#xff1a; 这是由于硬盘存在损坏导致的&#xff0c;可以通过系统自带的磁盘检查修复解决&#xff0c;而无需额外工具。 假设损坏的盘符是E&#xff0c;在命令行运行以下命令…

流媒体学习之路(WebRTC)——Pacer与GCC(5)

流媒体学习之路(WebRTC)——Pacer与GCC&#xff08;5&#xff09; —— 我正在的github给大家开发一个用于做实验的项目 —— github.com/qw225967/Bifrost目标&#xff1a;可以让大家熟悉各类Qos能力、带宽估计能力&#xff0c;提供每个环节关键参数调节接口并实现一个json全…

数据库-MySQL 启动方式

以管理员身份运行命令行 或者Shell net start //查看所有服务 net start MYSQL80 //启动服务 net stop MYSQL80 //停止服务完整安装MySQL社区版本的 会有这个 启动服务 停止服务 重启服务

C# 进阶语法,Linq入门到详解

什么是Linq LINQ (Language Integrated Query) 即语言集成查询–用来作查询一些操作类库主要负责对象的查询。 1、LINQ to Objects 主要负责对象的查询 2、LINQ to XML 主要负责XML的查询。 3、LINQ to ADO.NET 主要负责数据库的查询。 linq核心就是对数据源的操作 学linq另外…

15、Kubernetes核心技术 - 探针

目录 一、概述 二、探针类型 2.1、就绪探针&#xff08;Readiness Probe&#xff09; 2.2、存活探针&#xff08;Liveness Probe&#xff09; 三、探针探测方法 3.1、exec 3.2、httpGet 3.3、tcpSocket 四、探针配置项 五、探针使用 5.1、就绪探针&#xff08;Readin…

Java框架相关高频面试题

一&#xff0c;Spring 1&#xff0c;Spring框架中单例bean是线程安全的吗&#xff1f; 2&#xff0c;什么是AOP&#xff1f;你项目有用过吗&#xff1f; 3&#xff0c;Spring事务的失效场景有哪些&#xff1f; 发生自身调用&#xff08;类中使用this调用本类的方法&#xff0…

这货能大大增强ChatGpt的战斗力

今天我给你介绍一个能大大增强ChatGpt的战斗力的工具&#xff1a; gapier。 注册gapier ChatGpt推出了GPTs的功能&#xff0c;在创建GPTs的时候有个Actions的选项&#xff0c;是给我们调用第三方接口用的&#xff0c;以前一直不知道这么用。 直到我发现了一个网站&#xff1a…

11.盛水最多的容器(双指针,C解法)

题目描述&#xff1a; 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 说明&#xff1a;…

FX3U-1PG使用

作为扩展模块的安装 伺服驱动器的参数设置 1.设置为0&#xff0c;为位置模式&#xff0c;发送脉冲控制&#xff1b; 2. 设置旋转方向&#xff0c;以及脉冲方式&#xff0c;通常设置为01&#xff0c;因为FX3U-1PG只支持正方向脉冲负方向脉冲方式&#xff1b; 当然想改变电机运…

docker如何配置阿里云镜像加速?

登录阿里云后&#xff0c;我们点击右上角的控制台&#xff0c;控制台中搜索镜像加速服务&#xff0c;然后点击帮助文档的官方镜像加速&#xff1a; 点击容器镜像服务控制台&#xff1a; 在镜像工具里面的镜像加速器中就可以看到&#xff1a; 分别执行即可&#xff1a; 之后我们…

Docker与虚拟机的比对

在Windows操作系统上的对比&#xff1a; 但是官方还是建议我们尽量不要将Docker直接安装到Windows操作系统上。

k8s---声明式资源管理(yml文件)

在k8s当中支持两种声明资源的方式&#xff1a; 1、 yaml格式&#xff1a;主要用于和管理资源对象 2、 json格式&#xff1a;主要用于在API接口之间进行消息传递 声明式管理方法(yaml)文件 1、 适合对资源的修改操作 2、 声明式管理依赖于yaml文件&#xff0c;所有的内容都在y…

5大自动化测试的Python框架,看完就能涨薪5k 【实用干货】

目前&#xff0c;它在Tiobe指数中排名第三个&#xff0c;仅次于Java和C。随着该编程语言的广泛使用&#xff0c;基于Python的自动化测试框架也应运而生&#xff0c;且不断发展与丰富。 因此&#xff0c;开发与测试人员在为手头的项目选择测试框架时&#xff0c;需要考虑许多方…

《小学生》知网期刊投稿方式、投稿邮箱

《小学生》是国家新闻出版总署批准的正规期刊&#xff0c;杂志立足教育&#xff0c;服务全国&#xff0c;致力于为广大基础教育工作者搭建一个展示基础教育理论研究成果&#xff0c;交流经验、合作共进的学术平台。是广大专家、学者、教师、学子发表论文、交流信息的重要平台。…

【EI会议征稿通知】第三届艺术设计与数字化技术国际学术会议( ADDT 2024)

第三届艺术设计与数字化技术国际学术会议( ADDT 2024&#xff09; 2024 3rd International Conference on Art Design and Digital Technology 所谓艺术设计&#xff0c;就是将艺术的审美感应用到与日常生活密切相关的设计中&#xff0c;使其不仅具有审美功能&#xff0c;而且…

电风扇目标检测数据集VOC格式1100张

电风扇的全方位介绍 一、功能特性 电风扇作为一种晋及化的家用电器&#xff0c;其主要功能是利用电机驱动扇叶旋转&#xff0c;从而产生风力&#xff0c;用干调节室内空气流通&#xff0c;达至降温、通风和改善室内环境的目的。此外&#xff0c;现代电风扇还具备定时、遥控、…

阶段十-分布式-Redis02

第一章 Redis 事务 1.1 节 数据库事务复习 数据库事务的四大特性 A&#xff1a;Atomic &#xff0c;原子性&#xff0c;将所以SQL作为原子工作单元执行&#xff0c;要么全部执行&#xff0c;要么全部不执行&#xff1b;C&#xff1a;Consistent&#xff0c;一致性&#xff0…

day04 两两交换链表中的节点 删除链表的倒数第N个节点 链表相交 环形链表Ⅱ

题目1&#xff1a;24 两两交换链表中的节点 题目链接&#xff1a;24 两两交换链表中的节点 题意 两两交换链表中相邻的节点&#xff0c;返回交换后链表的头节点 虚拟头节点 注意终止条件&#xff0c;考虑节点的奇偶数&#xff0c;根据奇偶数确定终止条件 注意定义中间变量…

新闻稿发布:媒体重要还是价格重要

在当今信息爆炸的数字时代&#xff0c;企业推广与品牌塑造不可或缺的一环就是新闻稿发布。新闻稿是一种通过媒体渠道传递企业信息、宣传品牌、事件或产品新闻的文本形式。发布新闻稿的过程旨在将企业的声音传递给更广泛的受众&#xff0c;借助媒体平台实现品牌故事的广泛传播。…