本次介绍一种调用dll函数的通用简洁的方法,消除了原来调用方式的重复与繁琐,使得我们调用dll函数的方式更加方便简洁。用过dll的人会发现c++中调用dll中的函数有点繁琐,调用过程是这样的:在加载dll后还要定义一个对应的函数指针类型,再调用GetProcAddress获取函数地址,再转成函数指针,最后调用该函数。下面是调用dll中Max和Get函数的例子。

void TestDll()

{

typedef int(*pMax)(int a,int b);

typedef int(*pGet)(int a);

HINSTANCE hMode =LoadLibrary("MyDll.dll");if(hMode==nullptr)

return;

 

PMax Max = (PMax)GetProcAddress(hDLL,"Max");

if(Max==nullptr)

return;

 

int ret =Max(5,8); //8

 

PMin Get = (PMin)GetProcAddress(hDLL,"Get");

if(Get==nullptr)

return;

 

int ret =Get(5); //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中是什么函数都可以转换成这个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的方式更简洁通用。

再看看如何解决函数返回值和入参不统一的问题,关于这个问题,其实在前面的博文中就讲到了,不知道的童鞋看这里:

(原创)C++11改进我们的程序之简化我们的程序(一)

是的,还是通过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)(args...);

}

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

auto max = ExcecuteFunc<int(int, int)>("Max", 5, 8);
auto ret = ExcecuteFunc<int(int)>("Get", 5);

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

最后看看完整的代码吧。

#include <Windows.h>

#include <string>

using namespace std;

 

class DllParser

{

public:

 

DllParser()

{

}

 

~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 false;

 

auto b = FreeLibrary(m_hMod);

if (!b)

return false;

 

m_hMod = nullptr;

return true;

}

 

template <typename T>

std::function<T> GetFunction(const string&funcName)

{

FARPROC funAddress = GetProcAddress(m_hMod, funcName.c_str());

return std::function<T>((T*)(funAddress));

}

 

template <typename T, typename... Args>

typename std::result_of<std::function<T>(Args...)>::type ExcecuteFunc(const string& funcName,Args&&... args)

{

return GetFunction<T>(funcName)(args...);

}

 

private:

HMODULE m_hMod;

};

c++11 boost技术交流群:296561497,欢迎大家来交流技术。

一个简洁通用的调用DLL函数的帮助类的更多相关文章

  1. (原创)一个简洁通用的调用DLL函数的帮助类

    本次介绍一种调用dll函数的通用简洁的方法,消除了原来调用方式的重复与繁琐,使得我们调用dll函数的方式更加方便简洁.用过dll的人会发现c++中调用dll中的函数有点繁琐,调用过程是这样的:在加载d ...

  2. 在 C++Builder 工程里调用 DLL 函数

    调用 Visual C++ DLL 给 C++Builder 程序员提出了一些独特的挑战.在我们试图解决 Visual C++ 生成的 DLL 之前,回顾一下如何调用一个 C++Builder 创建的 ...

  3. 动态调用DLL函数有时正常,有时报Access violation的异常

    动态调用DLL函数有时正常,有时报Access violation的异常 typedef int (add *)(int a,int b); void test() {     hInst=LoadL ...

  4. ​c++ 调用DLL函数,出现错误

    ​c++ 调用DLL函数,出现错误 Run-Time Check Failure #0 - The value of ESP was not properly saved across a funct ...

  5. C++利用模板在Windows上快速调用DLL函数

    更新日志 --------- 2021/08/01 更新V2.2 增加 GetHmodule 函数 - 允许用户获取HMODULE以验证加载DLL是否成功. 2021/08/03 更新V2.3 增加 ...

  6. 一个简洁的PHP可逆加密函数(分享)

    http://www.jb51.net/article/38018.htm 本篇文章是对一个简洁的PHP可逆加密函数进行了详细的分析介绍,需要的朋友参考下   很多时候我们需要对数据进行加密解密,比如 ...

  7. GO语言 -- 调用DLL函数,填平所有的坑,最详尽攻略

    编译dll文件(源代码c++):g++ -shared main.cpp -o test.dll set GOARCH=386 第一个DLL函数,第一个参数,要求传入一个指针,直接指向[]byte类型 ...

  8. QT创建与调用Dll方法(包括类成员)--显式调用

    看网上的好多关于QT调用Dll的方法,大部分都是调用函数的,并没有调用C++类成员的情况,即使是有,比如说: 使用Qt编写模块化插件式应用程序 Qt 一步一步实现dll调用(附源码)---(这一篇里没 ...

  9. C++生成dll以及调用(函数)和类

    C++新手,方法可能有很多,此方法仅仅是自己实验并可行,详细步骤如下: 生成dll文件和lib文件: (1) 新建项目-windows桌面向导,选择动态链接.dll以及空项目: (2)复制代码(头文件 ...

随机推荐

  1. linux面试题1

    一.填空题:1. 在Linux系统中,以 文件 方式访问设备 .2. Linux内核引导时,从文件 /etc/fstab 中读取要加载的文件系统.3. Linux文件系统中每个文件用 i节点 来标识. ...

  2. 解决IE6下png图片不透明

    ie6着实是非常让人讨厌,显示一张图片,也要带着灰白色的背景色,一张好好的png图片就这么不透明了. 用n多中网上的方式,差点成功的就还有这个了 _background: none; _filter: ...

  3. [android]如何让TextView使用超链接

    找了很多网址,最后是这个有说到. 总的做法是: 1.(当然也可以从Res中获取.) tv.setText(Html.fromHtml("<a href=\"http://ww ...

  4. Unable to execute dex: method ID not in [0, 0xffff]: 65536

    http://ingramchen.io/blog/2014/09/prevention-of-android-dex-64k-method-size-limit.html

  5. Java中的10颗语法糖

    语法糖(Syntactic Sugar):也称糖衣语法,指在计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用.通常来说,使用语法糖能够增加程序的可读性,减少程序代码出错的 ...

  6. 我们究竟什么时候可以使用Ehcache缓存

    一.Ehcache是什么 EhCache是Hibernate的二级缓存技术之一,可以把查询出来的数据存储在内存或者磁盘,节省下次同样查询语句再次查询数据库,大幅减轻数据库压力. 二.Ehcache的使 ...

  7. [Irving] Wpf DevexPress GridControl 获取选中行

    WPF前台绑定事件代码: <RelayAction TargetControl="{Binding ElementName=GCInstoragePart}" MethodN ...

  8. 几种常见的FTP软件的二进制设置说明

    几种常见的FTP软件的二进制设置说明: 1.FlashFXP: 打开 FlashFXP:在工具栏中,选项 => 参数(也可以直接按F6键),在弹出来的窗口中,选择“传输(T)”卡,在传输模式中选 ...

  9. AAC 格式分析

    一直在做一个语音项目,到了测试阶段,近来不是很忙,想把之前做的内容整理一下. 关于AAC音频格式基本情况,可参考维基百科http://en.wikipedia.org/wiki/Advanced_Au ...

  10. 《Genesis-3D开源游戏引擎完整实例教程-2D射击游戏篇07:全屏炸弹》

    7.全屏炸弹 全屏炸弹概述: 为了增设游戏的趣味性,我们制作一个游戏的基本框架以外.还会增设一些其他的额外的功能.比如5秒无敌状态.冰冻效果等.下面咱们以消灭屏幕中所有炸弹为例,看除了碰撞可以触发事件 ...