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

在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:代码优化与工程级应用

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C DLL函数调用是指在C语言中调用动态链接库(DLL)中的函数DLL是一种用于存放代码和数据的文件格式,其中包含了可以被其他程序调用函数和变量。 实现C DLL函数调用的示例代码如下: 1. 创建DLL项目 首先,我们需要创建一个DLL项目。在IDE中选择新建项目,选择DLL(动态链接库)项目型,并为项目指定一个名称。 2. 编写DLL函数DLL项目中,编写我们需要被调用函数。例如,我们创建一个名为"add"的函数,在该函数实现两个整数相加的功能。 ```c __declspec(dllexport) int add(int a, int b) { return a + b; } ``` 3. 构建DLL 编译和构建DLL项目,生成一个名为"demo.dll"的DLL文件。 4. 创建调用DLL的C程序 在一个新的C程序项目中,我们可以使用以下代码调用刚刚创建的DLL中的函数: ```c #include <stdio.h> #include <windows.h> // 声明DLL函数原型 typedef int(*AddFunc)(int, int); int main() { // 加载DLL HINSTANCE hinstLib = LoadLibrary(TEXT("demo.dll")); // 如果DLL加载成功 if (hinstLib != NULL) { // 获取DLL函数地址 AddFunc addFunc = (AddFunc)GetProcAddress(hinstLib, "add"); // 如果获取函数地址成功 if (addFunc != NULL) { // 调用DLL函数 int result = addFunc(3, 5); printf("结果:%d\n", result); } // 释放DLL FreeLibrary(hinstLib); } else { printf("无法加载DLL\n"); } return 0; } ``` 以上代码中,我们首先加载DLL文件,然后使用"GetProcAddress"函数获取DLL函数"add"的地址,并将其转换为函数指针。接下来,我们可以使用函数指针直接调用DLL函数。 通过以上步骤,我们实现了C语言中调用DLL函数的功能。这使得我们可以将一些通用的功能封装成DLL,供其他程序调用,提高代码的可重用性和可维护性。同时,C DLL函数调用还可以实现与其他语言(如C++、C#等)的互操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值