如何优雅的导出函数
在开发过程中,经常会引用外部函数。方法主要有两种:
方法一:包含头文件并制定lib位置
- 优点:使用简单
- 缺点:lib和vs版本有关,不同的版本和编译模式可能导致编译失败
方法二:GetProcAddress
- 优点:和编译器无关,只要获取函数地址即可使用
- 缺点:需要一个个的函数去获取地址,相对麻烦
思考:有没有好的导出方式,可以直接包含头文件就使用呢?
- 答案:有,参考Miniblink。在使用Miniblink的时候我们只需要包含头文件就可以直接引用导出函数。
源码分析:
- 作为dll的开发者,需要导出函数。模式如下:
#if defined(__cplusplus)
#define XYCENTER_EXTERN_C extern "C"
#else
#define XYCENTER_EXTERN_C
#endif#这里定义宏,根据函数参数个数不同,定义如下函数,一直延续到NXYCENTER_DEFINE_ITERATORN
#define XYCENTER_DECLARE_ITERATOR0(returnVal, name, description) \XYCENTER_EXTERN_C __declspec(dllexport) returnVal name();#define XYCENTER_DECLARE_ITERATOR1(returnVal, name, p1, description) \XYCENTER_EXTERN_C __declspec(dllexport) returnVal name(p1);#define XYCENTER_DECLARE_ITERATOR2(returnVal, name, p1, p2, description) \XYCENTER_EXTERN_C __declspec(dllexport) returnVal name(p1, p2);#格式按照【返回类型】【函数名】【参数】来维护导出函数列表0-N
#define XYCENTER_FOR_EACH_DEFINE_FUNCTION(ITERATOR0, ITERATOR1,ITERATOR2) \ITERATOR0(void, xyShutdown, "") \ITERATOR1(void, set_xycenterlog_handle,char *,"") \
#导出所有的函数0-N
XYCENTER_FOR_EACH_DEFINE_FUNCTION(XYCENTER_DECLARE_ITERATOR0, XYCENTER_DECLARE_ITERATOR1)
实现对应的导出函数即可:
void xyShutdown(){}
void set_xycenterlog_handle(char *){}
- 作为dll的符号使用者,分为两个步骤定义函数指针和对象,获取对应的符号地址
#定义不同参数个数的函数指针以及对应的对象0-N
#selectany 关键字,相关含义可以自行搜索
#define XYCENTER_DEFINE_ITERATOR0(returnVal, name, description) \typedef returnVal(XYCENTER_CALL_TYPE* xyFN_##name)(); \__declspec(selectany) xyFN_##name name = ((xyFN_##name)0);#define XYCENTER_DEFINE_ITERATOR1(returnVal, name, p1, description) \typedef returnVal(XYCENTER_CALL_TYPE* xyFN_##name)(p1); \__declspec(selectany) xyFN_##name name = ((xyFN_##name)0);#define XYCENTER_DEFINE_ITERATOR2(returnVal, name, p1,p2, description) \typedef returnVal(XYCENTER_CALL_TYPE* xyFN_##name)(p1,p2); \__declspec(selectany) xyFN_##name name = ((xyFN_##name)0);#获取函数地址
#define XYCENTER_GET_PTR_ITERATOR(name) \name = (xyFN_##name)GetProcAddress(hMod, #name); \if (!name) \MessageBoxA(((HWND)0), #name##" api not found", #name, 0);
#为了兼容上述维护的函数列表,所以需要多对应获取0-N的参数模式,这里直接使用变参模式
#define XYCENTER_GET_PTR_ITERATORN(returnVal, name,...) \XYCENTER_GET_PTR_ITERATOR(name);#最后一步设置dll路径并且初始化
#for each 不同参数的函数地址
XYCENTER_FOR_EACH_DEFINE_FUNCTION(XYCENTER_DEFINE_ITERATOR0, XYCENTER_DEFINE_ITERATOR1, XYCENTER_DEFINE_ITERATOR2)__declspec(selectany) const wchar_t* s_xyCenterDllPath = L"xyCenter.dll";inline void xyCenterSetWkeDllPath(const wchar_t* dllPath)
{s_xyCenterDllPath = dllPath;
}inline int xyCenterInitialize()
{HMODULE hMod = NULL;if (!hMod)hMod = LoadLibraryW(s_xyCenterDllPath);if (hMod) {XYCENTER_FOR_EACH_DEFINE_FUNCTION(XYCENTER_GET_PTR_ITERATORN, XYCENTER_GET_PTR_ITERATORN, XYCENTER_GET_PTR_ITERATORN);return 1;}return 0;
}
完整头文件,目前默认支持最多10个参数,用户可以自己扩展
使用宏XYCENTER_EXPORTS
来决定是导入函数还是导出函数
#ifndef XYCENTER_DEFINE_H
#define XYCENTER_DEFINE_H#include <windows.h>//
#define XYCENTER_CALL_TYPE __cdecl#if defined(__cplusplus)
#define XYCENTER_EXTERN_C extern "C"
#else
#define XYCENTER_EXTERN_C
#endifnamespace xyCenter
{
#pragma pack(pop)/定义函数指针以及变量/
#define XYCENTER_DEFINE_ITERATOR0(returnVal, name, description) \typedef returnVal(XYCENTER_CALL_TYPE* xyFN_##name)(); \__declspec(selectany) xyFN_##name name = ((xyFN_##name)0);#define XYCENTER_DEFINE_ITERATOR1(returnVal, name, p1, description) \typedef returnVal(XYCENTER_CALL_TYPE* xyFN_##name)(p1); \__declspec(selectany) xyFN_##name name = ((xyFN_##name)0);#define XYCENTER_DEFINE_ITERATOR2(returnVal, name, p1,p2, description) \typedef returnVal(XYCENTER_CALL_TYPE* xyFN_##name)(p1,p2); \__declspec(selectany) xyFN_##name name = ((xyFN_##name)0);#define XYCENTER_DEFINE_ITERATOR3(returnVal, name, p1,p2,p3, description) \typedef returnVal(XYCENTER_CALL_TYPE* xyFN_##name)(p1,p2,p3); \__declspec(selectany) xyFN_##name name = ((xyFN_##name)0);#define XYCENTER_DEFINE_ITERATOR4(returnVal, name, p1,p2,p3,p4, description) \typedef returnVal(XYCENTER_CALL_TYPE* xyFN_##name)(p1,p2,p3,p4); \__declspec(selectany) xyFN_##name name = ((xyFN_##name)0);#define XYCENTER_DEFINE_ITERATOR5(returnVal, name, p1,p2,p3,p4,p5, description) \typedef returnVal(XYCENTER_CALL_TYPE* xyFN_##name)(p1,p2,p3,p4,p5); \__declspec(selectany) xyFN_##name name = ((xyFN_##name)0);#define XYCENTER_DEFINE_ITERATOR6(returnVal, name, p1,p2,p3,p4,p5,p6, description) \typedef returnVal(XYCENTER_CALL_TYPE* xyFN_##name)(p1,p2,p3,p4,p5,p6); \__declspec(selectany) xyFN_##name name = ((xyFN_##name)0);#define XYCENTER_DEFINE_ITERATOR7(returnVal, name, p1,p2,p3,p4,p5,p6,p7, description) \typedef returnVal(XYCENTER_CALL_TYPE* xyFN_##name)(p1,p2,p3,p4,p5,p6,p7); \__declspec(selectany) xyFN_##name name = ((xyFN_##name)0);#define XYCENTER_DEFINE_ITERATOR8(returnVal, name, p1,p2,p3,p4,p5,p6,p7,p8, description) \typedef returnVal(XYCENTER_CALL_TYPE* xyFN_##name)(p1,p2,p3,p4,p5,p6,p7,p8); \__declspec(selectany) xyFN_##name name = ((xyFN_##name)0);#define XYCENTER_DEFINE_ITERATOR9(returnVal, name, p1,p2,p3,p4,p5,p6,p7,p8,p9, description) \typedef returnVal(XYCENTER_CALL_TYPE* xyFN_##name)(p1,p2,p3,p4,p5,p6,p7,p8,p9); \__declspec(selectany) xyFN_##name name = ((xyFN_##name)0);#define XYCENTER_DEFINE_ITERATOR10(returnVal, name, p1,p2,p3,p4,p5,p6,p7,p8,p9,p10, description) \typedef returnVal(XYCENTER_CALL_TYPE* xyFN_##name)(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10); \__declspec(selectany) xyFN_##name name = ((xyFN_##name)0);/获取dll函数指针地址/
#define XYCENTER_GET_PTR_ITERATOR(name) \name = (xyFN_##name)GetProcAddress(hMod, #name); \if (!name) \MessageBoxA(((HWND)0), #name##" api not found", #name, 0);#define XYCENTER_GET_PTR_ITERATORN(returnVal, name,...) \XYCENTER_GET_PTR_ITERATOR(name);/定义导出函数/
#define XYCENTER_DECLARE_ITERATOR0(returnVal, name, description) \XYCENTER_EXTERN_C __declspec(dllexport) returnVal name();#define XYCENTER_DECLARE_ITERATOR1(returnVal, name, p1, description) \XYCENTER_EXTERN_C __declspec(dllexport) returnVal name(p1);#define XYCENTER_DECLARE_ITERATOR2(returnVal, name, p1, p2, description) \XYCENTER_EXTERN_C __declspec(dllexport) returnVal name(p1, p2);#define XYCENTER_DECLARE_ITERATOR3(returnVal, name, p1, p2,p3, description) \XYCENTER_EXTERN_C __declspec(dllexport) returnVal name(p1, p2,p3);#define XYCENTER_DECLARE_ITERATOR4(returnVal, name, p1, p2,p3,p4, description) \XYCENTER_EXTERN_C __declspec(dllexport) returnVal name(p1, p2,p3,p4);#define XYCENTER_DECLARE_ITERATOR5(returnVal, name, p1, p2,p3,p4,p5, description) \XYCENTER_EXTERN_C __declspec(dllexport) returnVal name(p1, p2,p3,p4,p5);#define XYCENTER_DECLARE_ITERATOR6(returnVal, name, p1, p2,p3,p4,p5,p6, description) \XYCENTER_EXTERN_C __declspec(dllexport) returnVal name(p1, p2,p3,p4,p5,p6);#define XYCENTER_DECLARE_ITERATOR7(returnVal, name, p1, p2,p3,p4,p5,p6,p7, description) \XYCENTER_EXTERN_C __declspec(dllexport) returnVal name(p1, p2,p3,p4,p5,p6,p7);#define XYCENTER_DECLARE_ITERATOR8(returnVal, name, p1, p2,p3,p4,p5,p6,p7,p8, description) \XYCENTER_EXTERN_C __declspec(dllexport) returnVal name(p1, p2,p3,p4,p5,p6,p7,p8);#define XYCENTER_DECLARE_ITERATOR9(returnVal, name, p1, p2,p3,p4,p5,p6,p7,p8,p9, description) \XYCENTER_EXTERN_C __declspec(dllexport) returnVal name(p1, p2,p3,p4,p5,p6,p7,p8,p9);#define XYCENTER_DECLARE_ITERATOR10(returnVal, name, p1, p2,p3,p4,p5,p6,p7,p8,p9,p10, description) \XYCENTER_EXTERN_C __declspec(dllexport) returnVal name(p1, p2,p3,p4,p5,p6,p7,p8,p9,p10);// 以下是xyCenter的导出函数。格式按照【返回类型】【函数名】【参数】来排列
#define XYCENTER_FOR_EACH_DEFINE_FUNCTION(ITERATOR0, ITERATOR1,ITERATOR2,ITERATOR3,ITERATOR4,ITERATOR5,ITERATOR6,ITERATOR7,ITERATOR8,ITERATOR9,ITERATOR10) \ITERATOR0(int, get_msgno, "获取通知消息的消息code") \ITERATOR1(void, set_log_handle, PFN_XYCENTERLOG_HANDLE,"设置日志句柄") \ITERATOR1(bool, write_data, XyCenterData *,"写入用户共享数据") \ITERATOR1(bool, read_data, XyCenterData&,"获取用户数据") \
//导出函数
#ifdef XYCENTER_EXPORTSXYCENTER_FOR_EACH_DEFINE_FUNCTION(XYCENTER_DECLARE_ITERATOR0, XYCENTER_DECLARE_ITERATOR1, XYCENTER_DECLARE_ITERATOR2,\XYCENTER_DECLARE_ITERATOR3,XYCENTER_DECLARE_ITERATOR4, XYCENTER_DECLARE_ITERATOR5, XYCENTER_DECLARE_ITERATOR6,\XYCENTER_DECLARE_ITERATOR7, XYCENTER_DECLARE_ITERATOR8,XYCENTER_DECLARE_ITERATOR9, XYCENTER_DECLARE_ITERATOR10)//引入导出函数
#elseXYCENTER_FOR_EACH_DEFINE_FUNCTION(XYCENTER_DEFINE_ITERATOR0, XYCENTER_DEFINE_ITERATOR1, XYCENTER_DEFINE_ITERATOR2,\XYCENTER_DEFINE_ITERATOR3, XYCENTER_DEFINE_ITERATOR4, XYCENTER_DEFINE_ITERATOR5, XYCENTER_DEFINE_ITERATOR6,\XYCENTER_DEFINE_ITERATOR7, XYCENTER_DEFINE_ITERATOR8, XYCENTER_DEFINE_ITERATOR9, XYCENTER_DEFINE_ITERATOR10)__declspec(selectany) const wchar_t* s_xyCenterDllPath = L"xyCenter.dll";inline void SetWkeDllPath(const wchar_t* dllPath){s_xyCenterDllPath = dllPath;}inline bool Initialize(){HMODULE hMod = NULL;if (!hMod)hMod = LoadLibraryW(s_xyCenterDllPath);if (hMod) {XYCENTER_FOR_EACH_DEFINE_FUNCTION(XYCENTER_GET_PTR_ITERATORN, XYCENTER_GET_PTR_ITERATORN, XYCENTER_GET_PTR_ITERATORN,\XYCENTER_GET_PTR_ITERATORN, XYCENTER_GET_PTR_ITERATORN, XYCENTER_GET_PTR_ITERATORN, XYCENTER_GET_PTR_ITERATORN,\XYCENTER_GET_PTR_ITERATORN, XYCENTER_GET_PTR_ITERATORN, XYCENTER_GET_PTR_ITERATORN, XYCENTER_GET_PTR_ITERATORN);return true;}return false;}
#endif // DEBUG#endif // XYCENTER_DEFINE_H};
此时我们只需要包含头文件,并且初始化一下即可直接使用导出函数。
相关文章:
如何优雅的导出函数
在开发过程中,经常会引用外部函数。方法主要有两种: 方法一:包含头文件并制定lib位置 优点:使用简单缺点:lib和vs版本有关,不同的版本和编译模式可能导致编译失败 方法二:GetProcAddress 优…...
c++多重继承
1.概论多重继承是否有必要吗?这个问题显然是一个哲学问题,正确的解答方式是根据情况来看,有时候需要,有时候不需要,这显然是一句废话,有点像上马克思主义哲学或者中庸思。但是这个问题和那些思想一样&#…...

15_FreeRtos计数信号量优先级翻转互斥信号量
目录 计数型信号量 计数型信号量相关API函数 计数型信号量实验源码 优先级翻转简介 优先级翻转实验源码 互斥信号量 互斥信号量相关API函数 互斥信号量实验源码 计数型信号量 计数型信号量相当于队列长度大于1的队列,因此计数型信号量能够容纳多个资源,这在…...

二叉树(一)
二叉树(一)1.树的概念2.树的相关概念3.树的表示4.树在实际中的运用5.二叉树概念及结构6.特殊的二叉树7.二叉树的性质🌟🌟hello,各位读者大大们你们好呀🌟🌟 🚀🚀系列专栏…...

【SCL】1200案例:天塔之光数码管显示液体混合水塔水位
使用scl编写天塔之光&数码管显示&液体混合&水塔水位 文章目录 目录 文章目录 前言 一、案例1:天塔之光 1.控制要求 2.编写程序 3.效果 二、案例2:液体混合 1.控制要求 2.编写程序 三、案例3:数码管显示 1.控制要求 2.编写程序 3…...

5.1配置IBGP和EBGP
5.2.1实验1:配置IBGP和EBGP 实验目的 熟悉IBGP和EBGP的应用场景掌握IBGP和EBGP的配置方法 实验拓扑 实验拓扑如图5-1所示: 图5-1:配置IBGP和EBGP 实验步骤 IP地址的配置 R1的配置 <Huawei>system-view Enter system view, return …...

c++中超级详细的一些知识,新手快来
目录 2.文章内容简介 3.理解虚函数表 3.1.多态与虚表 3.2.使用指针访问虚表 4.对象模型概述 4.1.简单对象模型 4.2.表格驱动模型 4.3.非继承下的C对象模型 5.继承下的C对象模型 5.1.单继承 5.2.多继承 5.2.1一般的多重继承(非菱形继承) 5.2…...

[答疑]经营困难时期谈建模和伪创新-长点心和长点良心
leonll 2022-11-26 9:53 我们今年真是太难了……(此处删除若干字)……去年底就想着邀请您来给我们讲课,现在也没有实行。我想再和我们老大提,您觉得怎么说个关键理由,这样的形势合适引进UML开发流程? UML…...

计算机基础知识
计算机网络的拓扑结构 一、OSI 7层网络模型是指什么? 7层分别是什么?每层的作用是什么? OSI7层模型是 国际标准化组织(ISO)制定的一个用于计算机或通信系统间互联的标准体系。 每层功能:(自底向上) 物理层:建立、…...

Java爬虫—WebMagic
一,WebMagic介绍WebMagic企业开发,比HttpClient和JSoup更方便一),WebMagic架构介绍WebMagic有DownLoad,PageProcessor,Schedule,Pipeline四大组件,并有Spider将他们组织起来…...

[软件工程导论(第六版)]第2章 可行性研究(复习笔记)
文章目录2.1 可行性研究的任务2.2 可行性研究过程2.3 系统流程图2.4 数据流图概念2.5 数据字典2.6 成本/效益分析2.1 可行性研究的任务 可行性研究的目的 用最小的代价在尽可能短的时间内确定问题是否能够解决。 可行性研究的3个方面 (1)技术可行性&…...

Mac下安装Tomcat以及IDEA中的配置
安装brew 打开终端输入以下命令: /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 搜索tomcat版本,输入以下命令: brew search tomcat 安装自己想要的版本,例…...

【Linux详解】——文件基础(I/O、文件描述符、重定向、缓冲区)
📖 前言:本期介绍文件基础I/O。 目录🕒 1. 文件回顾🕘 1.1 基本概念🕘 1.2 C语言文件操作🕤 1.2.1 概述🕤 1.2.2 实操🕤 1.2.3 OS接口open的使用(比特位标记)…...
HomMat2d
1.affine_trans_region(区域的任意变换) 2.hom_mat2d_identity(创建二位变换矩阵) 3.hom_mat2d_translate(平移) 4.hom_mat2d_scale(缩放) 5.hom_mat2d_rotate(旋转 &…...

Python3 JSON 数据解析
Python3 JSON 数据解析 JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式。 Python3 中可以使用 json 模块来对 JSON 数据进行编解码,它包含了两个函数: json.dumps(): 对数据进行编码。json.loads(): 对数据进行解码。 在 json 的编解码…...

Homebrew 安装遇到的问题
Homebrew 安装遇到的问题 例如:第一章 Python 机器学习入门之pandas的使用 文章目录Homebrew 安装遇到的问题前言一、安装二、遇到的问题1.提示 zsh: command not found: brew三、解决问题前言 使用 Homebrew 能够 安装 Apple(或您的 Linux 系统&#…...

Metasploit框架基础(二)
文章目录前言一、Meatsplooit的架构二、目录结构datadocumentationlibmodulesplugins三、Measploit模块四、Metasploit的使用前言 Metasploit是用ruby语言开发的,所以你打开软件目录,会发现很多.rb结尾的文件。ruby是一门OOP的语言。 一、Meatsplooit的…...

c++容器
1、vector容器 1.1性质 a)该容器的数据结构和数组相似,被称为单端数组。 b)在存储数据时不是在原有空间上往后拓展,而是找到一个新的空间,将原数据深拷贝到新空间,释放原空间。该过程被称为动态拓展。 vec…...
Vue.js如何实现对一千张图片进行分页加载?
目录 vue处理一千张图片进行分页加载 分页加载、懒加载---概念介绍: 思路: 开发过程中,如果后端一次性返回你1000多条图片或数据,那我们前端应该怎么用什么思路去更好的渲染呢? 第一种:我们可以使用分页…...
计算机网络复习(六)
考点:MIME及其编码(base64,quoted-printable)网络协议http是基于什么协议,应用层到网络层基于什么协议6-27.试将数据 11001100 10000001 00111000 进行 base64 编码,并得到最后传输的 ASCII 数据。答:先将 24 比特的二…...

IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...

【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...

CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...

Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...