C++11实现一个加载dll并调用其中函数的dll帮助类

C++学习记录006——调用DLL(动态) C++ 动态调用动态链接库 阅读详情

在C++中调用dll中的函数比较繁琐,调用过程如下:在加载dll后还需要定义一个对应的函数指针类型,接着调用GetProcAddress获取函数地址,再转成函数指针,最后调用该函数。如下:

void TestDll()
{
	typedef int(*pMax)(int a, int b);
	typedef int(*pGet)(int a);

	HINSTANCE hDll = LoadLibraryA("mydll.dll");
	if (hDll == nullptr)
		return;

	pMax Max = (pMax)GetProcAddress(hDll, "Max");
	if (Max == nullptr)
		return;

	int ret = Max(5, 8);

	pGet Get = (pGet)GetProcAddress(hDll, "Get");
	if (Get == nullptr)
		return;

	int ret = Get(5);

	FreeLibrary(hDll);
}

这段代码看起来很繁琐,因为每用一个函数就需要先定义一个函数指针,然后再根据名称获取函数地址,最后调用。如果一个dll中有上百个函数,这种繁琐的定义会让人不胜其烦。其实获取函数地址和调用函数的过程是重复逻辑,应该消除,我们不希望每次都定义一个函数指针和调用GetProcAddress,应该用一种简洁通用的方式去调用dll中的函数。我们希望调用dll中的函数就像调用普通的函数一样,传入一个函数名称和函数的参数就可以实现函数的调用,类似于:

Ret CallDllFunc(const string& funName, T arg);

如果以这种方式调用,就能避免繁琐的函数指针定义以及反复地调用GetProcAddress了。

下面介绍一种可行的解决方案,如果按照Ret CallDllFunc(const string& funName, T arg);这种方式调用,首先要把函数指针转换成一种函数对象或泛型函数,这里可以用std::function去做这个事情,即通过一个函数封装GetProcAddress,这样通过函数名称就能获取一个泛型函数std::function,希望这个function是通用的,不论dll中是什么函数都可以转换成这个std::function,最后调用这个通用的function即可。但是调用这个通用的function还有两个问题需解决:

  1. 函数的返回值可能是不同类型,如果以一种通用的返回值来消除这种不同返回值导致的差异呢?
  2. 函数的入参数目可能任意个数,且类型也不尽相同,如何来消除入参个数和类型的差异呢?

首先看一下如何封装GetProcAddress,将函数指针转换成std::function,代码如下:

template <typename T>
std::function<T> GetFunction(const string& funcName)
{
	FARPROC funAddress = GetProcAddress(m_hMod, funcName.c_str());
	return std::function<T>((T*)(funAddress));
}

其中T是std::function的模板参数,即函数类型的签名。如果要获取上面例子中的Max和Get函数,可以这样:

auto fmax = GetFunction<int(int, int)>("Max");
auto fget = GetFunction<int(int)>("Get");

这种方式比之前先定义函数指针在调用GetProcAddress的方式更简洁通用。

再看看如何解决函数返回值和入参不统一的问题,通过result_of和可变参数模板来解决,最终的调用函数如下:

template <typename T, typename... Args>
typename std::result_of<std::function<T>(Args...)>::type ExcecuteFunc(const string& funcName, Args&&... args)
{
	return GetFunction<T>(funcName)(arg...);
}

上面的例子中要调用Max和Get函数,这样就行了:

auto max = ExecuteFunc<int(int, int)>("Max", 5, 8);
auto ret = ExecuteFunc<int(int)>("Ret", 5);

比之前的调用方式简洁直观多了,没有了繁琐的函数指针的定义,没有了反复的调用GetProcAddress及其转换和调用。

完整代码如下:

#pragma once
#include <Windows.h>
#include <string>
#include <map>
#include <functional>
using namespace std;

class DllParser
{
public:

	DllParser():m_hMod(nullptr)
	{
	}

	~DllParser()
	{
		UnLoad();
	}

	bool Load(const string& dllPath)
	{
		m_hMod = LoadLibraryA(dllPath.data());
		if (nullptr == m_hMod)
		{
			printf("LoadLibrary failed\n");
			return false;
		}

		return true;
	}

	bool UnLoad()
	{
		if (m_hMod == nullptr)
			return true;

		auto b = FreeLibrary(m_hMod);
		if (!b)
			return false;

		m_hMod = nullptr;
		return true;
	}

	template <typename T>
	std::function<T> GetFunction(const string& funcName)
	{
		auto it = m_map.find(funcName);
		if (it == m_map.end())
		{
			auto addr = GetProcAddress(m_hMod, funcName.c_str());
			if (!addr)
				return nullptr;
			m_map.insert(std::make_pair(funcName, addr));
			it = m_map.find(funcName);
		}
		
		return std::function<T>((T*) (it->second));
	}

	template <typename T, typename... Args>
	typename std::result_of<std::function<T>(Args...)>::type ExcecuteFunc(const string& funcName, Args&&... args)
	{
		auto f = GetFunction<T>(funcName);
		if (f == nullptr)
		{
			string s = "can not find this function " + funcName;
			throw std::exception(s.c_str());
		}			

		return f(std::forward<Args>(args)...);
	}

private:
	HMODULE m_hMod;
	std::map<string, FARPROC> m_map;
};

实现的关键是如何将一个FARPROC变成一个函数指针复制给std::function,然后再调用可变参数执行。函数的返回值通过std::result_of来泛化,使得不同的返回值的dll函数都可以用相同的方式来调用。

 

参考资料:

《深入应用C++11:代码优化与工程级应用》

C#调用C++的dll文件 目录 1.C#调用C++(类)的dll文件 C#端的代码: C++端的代码: 被调用的类的头文件 被调用类的实现SampleCppClass.cpp 封装成dll的头文件 封装成dll的实现 2.使用委托调用 1.C#调用C++(类)的dll文件 C#端的代码: using System; using System.Collections.Generic; using S... 阅读详情

相关推荐

如何在Delphi 程序中调用任意 C++ DLL动态链接库

互联网上有大量有用的 C++ 库。我们也在这个博客上展示了C++ 的许多伟大用途。C++ 通常具有极高的性能。如果我们有 C++ 库的源代码,我们就可以创建一个包,然后我们就可以在 Delphi 程序中使用 C++。尽管 C++ 库的源代码通常不可用。在商业 C++ 库中,通常只获得几个 C++ 头文件和静态库文件 (.lib),而没有任何随附的 .cpp 源文件。因此,在这种情况下,当我们想在 Delphi 应用程序中使用那些 C++ 库时,我们可以使用代理 DLL 来实现。 如何创建一个代理...

Delphi开发局QQ群:32422310 3904

C++动态链接库DLL文件的加载

本节内容1、如何从DLL中获得资源(MFC DLL)2、如何使用DEF文件导出函数3、如何显式链接DLL4、如何隐式链接DLL5、如何在DLL中共享数据6、如何在DLL中使用对话框资源(MFC DLL)7、如何在MFC扩展DLL中导出类 1、如何从DLL中获得资源(MFC DLL) LoadLibrary+::LoadString/::LoadIcon/::LoadBitmap HINSTANCE hModule = LoadLibrary(_T("test.dll")); HBITMAP

爱看书的小沐 6876

【10】C#实战篇——C# 调用 C++ dll(C++ 导出函数、C++导出类)

本文介绍了C#调用C++ DLL的方法,重点演示了如何导出C++类函数和普通函数。通过宏定义MATHTEST_API实现DLL导出,并使用extern "C"确保C/C++通用调用。文章提供了完整的.h和.cpp文件示例,展示了一个包含加减乘除功能的数学类及其导出方法。在C#调用部分,使用DllImport特性导入DLL函数,并通过IntPtr处理C++类实例。最后还列出了C++与C#常见数据类型的对应关系表,为跨语言调用提供参考。整个过程涵盖了DLL创建、导出、调用及内存管理的完整流程

程序员,他们想的是什么?他们想的永远都是技术,他们崇尚的也永远都是技术。 990

C++ 调用dll的方法

在C++中,为了支持重载机制,在编译生成的汇编码中,要对函数的名字进行一些处理,加入比如函数的返回类型等等.而在C中,只是简单的函数名字而已,不会加入其他的信息.也就是说:C++和C对产生的函数名字的处理是不一样的。dll的开发者可以通过dll隐藏接口的代码实现,比如你只想让其他人使用你的算法,但不想让其他人知道你的算法和逻辑代码,就可以把它们封装到dll中。动态调用不是链接时完成的,而是在运行时完成的,动态调用不会在可执行文件中写入DLL相关的信息,而是直接调用dll中的函数。

Sakuya__的博客 6万+

Unity中C#调用C++封装的dll,接收并传递结构体数组指针(类和结构体)

项目所需,需要在unity中调用C++封装的dll,需要传递结构体数组。C++中封装的函数的参数为结构体指针。在网上找了一下博客,然后结合MSDN上的C#编程参考手册,找到了方法。 DLL的创建 这里创建了一个.h文件,声明了很简单两个结构体和一个类,类中还声明了一个Vector6D的数组,没有使用更复杂的结构体。 //StructTest.h #ifndef _STRUCTTEST_H_ #define _STRUCTTEST_H_ #include <iostream> #include &

JSBYWUYR的博客 2980

C\C++ 加载dll并调用dll中的函数

加载dll并调用dll中的函数。

王牌飞行员_里海的博客 3147

【C++】DLL文件加载的两种方式

关于C++加载DLL文件的方式

weixin_44984705的博客 3963

ESP-Mesh-Lite vs ESP-Mesh:如何选择适合你的物联网组网方案?

本文深入对比了乐鑫ESP-Mesh-Lite与ESP-Mesh两种物联网组网方案的核心差异。重点解析了ESP-Mesh-Lite分布式协议栈架构的优势,即每个节点拥有独立IP,可直接访问外部网络,极大简化了应用层开发。文章从网络架构、性能、开发体验及适用场景等多维度进行分析,为开发者根据项目需求(如分布式数据采集、智能家居或集中控制网络)选择最合适的方案提供了清晰的决策框架。

pluto的博客 815

VS 2015用C++项目生成dll并调用(2021.3.1)

Visual Studio 2015 的dll生成及调用 2021.2.28环境DLL介绍生成dll新建DLL项目调用dll 环境 Visual Studio 2015 DLL介绍 生成dll 新建DLL项目         第一步,打开VS 2015,选择已安装下的Visual C++后,点击右侧的DLL(通用 Windows 8.1),输入项目名称和位置,点击确定; 调用dll ...

I have a dream, here my dreams come true! 1万+

使用Intel编译器编译OpenFOAM

本文总结了使用INTEL编译器编译OpenFOAM的过程

姜蜉蝣的博客 1546

从多项式到神经网络:DPD算法演进史及其在RRU中的工程实现

本文系统梳理了数字预失真(DPD)算法从经典多项式模型、查表法到神经网络驱动的演进历程,并深入探讨了其在基站射频单元(RRU)中面临的硬件资源约束与工程实现挑战。文章为通信设备的技术决策者提供了算法选型、性能权衡及混合架构设计的实用评估框架,助力在性能、功耗与成本间找到最优平衡。

cherry的博客 1076

CANoe DLL编程(一)—— Visual Studio 创建DLL以及动态调用

visual studio 创建DLL以及动态调用

蚂蚁小兵 1万+

网络基础(二):数制转换与分类编址的IPv4地址

本文介绍数制转换、IP地址、分类编址的IPv4地址,希望对你有帮助!

十七拾的博客 1052

pybind11 实现 Python 调用 C++ 函数

用来生成 Python 模块。

2302_80010023的博客 1039

Hi3516CV610 YOLOv8模型转换与部署实战:从源码到板端推理全流程解析

本文详细解析了将YOLOv8模型部署到Hi3516CV610开发板的全流程。内容涵盖从Ubuntu环境搭建、模型获取与验证,到关键的PyTorch模型转换为ONNX格式,并利用海思工具链量化生成板端可执行的.wk文件。最后,指导开发者编写板端C程序,完成视频流处理、NNIE推理及结果后处理,并提供了部署调试与性能优化的实用技巧。

weixin_29215815的博客 268

【推荐100个unity插件】性能极高的逼真爆炸、火焰和烟雾效果——UNI VFX

这是一篇关于Unity中高性能GPU粒子特效插件UNI VFX的使用指南。文章介绍了该插件提供逼真的爆炸、火焰和烟雾效果,支持URP和HDRP渲染管线。主要内容包括:插件安装方法(需预先安装Visual Effect Graph和Shader Graph包)、事件系统使用方法(如create/loop/stop等事件控制)、特效分类(自动结束型和持续型特效)、检视面板配置技巧以及性能优化建议(特别提醒灯光特效对性能的影响较大)。文末还推荐了多个Unity开发相关的专栏内容。

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 1487

C++:VS2017 C++ 生成dll并调用 示例

一、生成dll 1.1 新建项目 项目类型为“动态链接库(DLL)”,名称为“xj”;Release,x64 1.2 添加头文件“xj.h” 1.3 修改属性 右击项目—配置属性—C/C++—预编译头—不使用预编译头 1.4 示例 1.4.1 示例1:整数相加的函数 1.4.2 示例2:利用OpenCV读取图片高和宽的函数(可忽略) (需添加包含目录和附加依赖项,如下...

累了就要打游戏 2378

2025年前端框架全景解析:React、Vue、Angular的生态与未来之争

前端框架的竞争本质是工程哲学的分野:React的灵活、Vue的简洁、Angular的严谨,分别对应不同场景需求。

科技D人生 4843

Java使用JNA调用C++动态库dll,并得到C++传过来的对象数组值(超级详细)

Java使用JNA调用C++动态库dll,并得到C++传过来的对象数组值(超级详细)

qq_44161818的博客 5317

网页内容抓取的艺术:使用R语言解析动态网页

本文探讨了使用R语言处理动态网页数据的方法。针对AJAX加载的学术网站,传统的静态抓取和RSelenium方法存在会话终止、浏览器错误等问题。通过分析发现内容通过admin-ajax.php加载,提出使用httr2模拟POST请求获取数据,配合rvest解析HTML内容。该方法避免了浏览器自动化的弊端,实现了高效稳定的动态内容提取,为处理类似AJAX网站提供了通用解决方案。

m0_74337424的博客 52

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

QT调用C++写的Dll

C#写的dll是没有dllMain入口函数的,是一种中间语言,需要.Net运行时进行做本地化工作,因此如果要调用C#写的dll,需要依赖.Net运行时,然而Qt中还无法直接调用.Net运行时,最好的方式是能够在Qt中直接调用C#dll的函数,但是Qt明显只能调用C++写的dll,所以就只能通过编写一个C++的dll导出接口供Qt调用,这个C++编写的dll对C#写的dll进行封装,这个C++的dll可以采用/CLR方式对C#编写的dll进行引用的,即将C++编写的dll中生成的.lib文件供Qt进行链接,由

u011555996的博客 3566

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Win11下python 调用C++动态链接库dll

由于代码中包含C语言,需要用到extern "C"关键字,指示编译器这部分代码按 C 语言的进行编译,而不是 C++。用于将函数、数据、类或类成员函数从动态链接库(DLL)中导出,使得其他程序或模块能够调用和使用这些导出的函数或数据。如下图所示,首先在项目上右击点击属性,配置选择Release, 平台选择x64,配置类型选择动态库dll。在一些耗时的函数上考虑使用C++进行加速,涉及把cpp文件转为dll,供python来调用。例如,C++语言代码中输出的是一个结构体。其次,在DLL在调用的函数前添加。

shuaijieer的博客 748

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

c++相关函数和类的dll导入及导出(VScode)

以sayHello()函数为例,如何把一个函数导出一个动态库。

LCQDAYTOY的博客 2672

利用C++11可变模板,封装调用dll导出函数

起因 开发中经常需要动态调用一些导出函数,试着利用C++11特性封装一下 尝试 常规使用 typedef int WINAPI (*TMessageBoxA)(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType); if (HMODULE m = LoadLibraryA("user32")) { if (FARPROC pro...

avxiut3014的博客 370

Qt调用dll中的功能函数

声明: 事先我已经自己动手写了一个简单的dll文件(myDLL.dll),C版接口的。并且用我前两篇有关DLL文章里面的方法,从dll中导出了导入库(.lib)文件,dll中有两个函数,原型如下:voidHelloWorld(); //函数内部调用Win32 API,功能是弹出一个helloworld提示框intadd(inta,intb); //实现两个数相加,并返...

weixin_30666401的博客 1662
上一篇: UML类图
下一篇: C++11 宽窄字符转换
CoderAldrich
博客等级 码龄12年 431粉丝 24原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值