1.问题描述

比如现在有一个函数叫MyFunc,现在想通过函数名调用该函数,该怎么办呢?

2.概述

如果这个问题是JAVA语言,我们会很自然的想到反射来解决这个问题,但是c++中没有反射,该怎么办呢?

这个问题需要用到c++动态库dll的创建和解析,通过这两个知识点,可以解决以上问题,下里面我们详细讲述

3.dll库的动态调用和解析

生成动态链接库(dll文件)

1、使用VS生成动态链接库的步骤:
(1)新建一个win32控制台工程,并在应用程序设置窗口中选择“Dll”选项,附加选项选择“空项目”。如下图:

(2)创建完工程之后,添加源文件,在源文件中写上想导出到dll文件的函数。函数声明之前应该加上“_declpec(dllexport)”表示函数输出为动态链接库。除此之外,还要在函数名前面加上调用约定。因为c/c++语言默认的调用约定是“_cdecl”,如果采用“_cdecl”调用约定,可以不用写。如果使用“_stdcall”和“_fastcall”调用约定,则要进行说明。下图是一个简单的例子:

图中有三个函数,分别采用的调用约定是“_cdecl”,”_stdcall”和”_fastcall”。调用约定会给函数名加上一些修饰,不同的调用约定给函数名的修饰是不一样的,因此要慎重地使用调用约定。
(3)编译。在菜单栏上的“生成”中点击“生成解决方案”即可生成动态链接库。如果编译成功,到工程文件夹下面的Debug文件夹里头可以找到后缀名为dll和lib连个文件。其中,lib文件保存着函数的相关定义和索引,其作用类似于头文件,而dll文件是函数的实现部分,是不可缺少的。

2、生成动态链接库时应注意的事项
(1)函数声明前面加上“_declspec(dllexport)”表明函数将输出为动态链接库,是必不可少的,
(2)导出的函数如果不是采用C/C++默认的“_cdecl”的调用约定,则要特别说明。使用调用约定时,应考虑到以后调用该函数的问题,调用时使用的调用约定只有与生成时设置的调用约定相一致时,才能调用。也就是说,如果生成dll文件时,给函数设置的调用约定为“_stdcall”,而调用该函数时使用的调用约定是“_cdecl”,那么将会无法找到该函数。
(3)在相同的调用约定下,采用不同的编译器,对函数名的修饰是不一样的。比如,同是采用”_cdecl”调用约定,C语言和C++语言导出的dll文件中,函数的修饰名是不一样的。如果要C语言风格的dll文件,就要再加上“extern C”进行修饰,或者把源文件名的后缀改为.c。如果是要C++风格的dll文件,则源文件名后缀必须为.cpp。下图是生成C风格的dll文件例子:

前两个函数将会导出为C风格的dll,而后一个函数被导出为C++风格的dll。如果把源文件后缀改为.c,那么所有的函数都会被导出为C风格的dll。

调用动态链接库

1、C语言调用C语言的dll文件
如图,有三个函数被导出到dll,前两个是C语言风格的,后一个是C++风格的。C语言是无法用常规方法调用C++风格的dll。

(1)新建一个控制台工程,添加一个源文件,并将源文件的后缀改为.c,告诉编译器这是一个C语言程序。
(2)将lib文件和dll文件放在与源文件相同的目录下。
(3)在程序的开头要加上#pragma comment(lib,”mydll.lib”),第一个参数必须是lib,第二个个参数是lib文件的文件名。函数调用前要先声明,函数的声明需要加上调用约定修饰。如下图:

(4)生成解决方案,如果没有错误,运行程序将会输出正确的结果。

2、C++调用C语言的dll
在C++程序中,要调用C语言的dll,要声明一下调用的函数是C语言风格的。方法是在函数声明时加上 extern “C”修饰。新建一个控制台工程,添加一个cpp文件,源文件中的代码如图所示:

3、C++调用C++的dll

C++调用C++的dll,只需在函数声明时加上调用约定修饰。如下图:

4、动态加载dll
以上的调用dll的方法都是属于静态调用类型的,一般是需要有lib文件的。如果采用动态加载dll,则不需要lib文件,只需dll文件就足够了。动态加载dll需要用到两个函数,一个是LoadLibrary,另一个是GetProcAddress,这两个函数都包含在window.h头文件中。值得注意的是,动态加载dll文件的方法,一般只能调用C语言风格的,且调用约定为“_cdecl”的函数。下图是动态加载dll的例子:

上面说,动态加载dll的方法一般适合调用C风格的、且调用约定为“_cdecl”的函数,那是因为C风格的、且调用约定为“_cdecl”的函数的函数名不会被修饰,源文件写的是什么样子,dll文件中就是什么样子。当然,调用C++风格的,且不是“_cdecl”约定的函数也是可以的,只是很麻烦。由于编译器类型(C或C++)和调用约定都会对函数的名称进行修饰,使得dll文件中的函数名称不再是源文件所写的那样。GetProcAddress函数是通过函数名来查找函数入口的,因此,只要知道dll文件中的函数修饰名,将函数修饰名传给GetProcAddress函数,就可以获得函数的指针。那么如何知道dll文件中函数的修饰名呢?这就需要用到一些分析软件了,比如depends这个软件就可以查看dll文件的函数名称。下图是使用depends软件查看dll中的函数。可以看到Add和Multi函数在dll文件中的修饰名分别是?Add@@YGHHH@Z和?Multi@@YGHHH@Z。

是不是所有的函数都可以通过查找其在dll文件中的修饰名来获取函数指针呢?为此,我做了一些实验,实验未必充分,但也可以得出一些结论:
(1)往GetProcAddress函数中传入函数在dll的修饰名,如果dll中的函数采用的是“_cdecl”调用约定,无论是C风格的还是C++风格的,都不会报错,函数调用的结果也是正确的。下图是调用采用“_cdecl”调用约定的函数:

从实验结果来看,对于调用约定为“_cdecl”的函数,只要能通过depends找到函数的修饰名,就可以调用该函数。函数调用的结果是正确的。

(2)往GetProcAddress函数中传入函数在dll的修饰名,如果dll中的函数采用的是“_stdcall”调用约定,程序运行时会报错,但是忽略错误,调用的结果却是正确的。下图是调用“_stdcall”约定的函数:


从实验结果来看,调用“_stdcall”约定的函数,是会报错的,但结果仍然正确。

(3)往GetProcAddress函数中传入函数在dll的修饰名,如果dll中的函数采用的是“_fastcall”调用约定,那么程序运行不会报错,但调用结果却是错误的!下图是调用“_fastcall”约定的函数:


很让人郁闷的是,调用“_fastcall”约定的函数,程序运行时不会报错,但是调用的结果却是错得离谱。2+3=-1672607445这是什么鬼?原因不明。

(4)结论:通过使用LoadLibrary函数和GetProcAddress函数来动态加载dll文件,这种方法只适用于调用“_cedcl”约定的函数,只要是采用“_cdecl”约定的,不管是C风格的还是C++风格的,都可以正常地被调用。如果是其他调用约定,无论是C风格还是C++风格的函数,都无法正常调用。

4.有了这种方法,我们就可解决这个问题了,附带2篇动态库和静态库的联系和区别知识点:

文章一:   .dll和.lib文件:https://www.cnblogs.com/How-Come/p/11059674.html

文章二:    手把手教你如何制作和使用lib和dll :https://blog.csdn.net/sj2050/article/details/81700183

如何通过函数名的字符串运行函数(从dll动态库谈起,使用c++)相关推荐

  1. python 使用函数名的字符串调用函数(4种方法)

    先看一个例子: >>> def foo():print "foo">>> def bar():print "bar"> ...

  2. C++之指针探究(十一):函数名的本质和函数指针

    相关博文:C++之指针探究(十三):函数指针数组 相关博文:C++之指针探究(十二):指针.下标.数组及其作函数参数探究 相关博文:C++之指针探究(十一):函数名的本质和函数指针 相关博文:C++之 ...

  3. linux打印函数名,linux kernel 打印函数指针对应的函数名方法

    linux kernel 打印函数指针对应的函数名方法 内核中函数指针用的很多,在debug 的时候能直接打印出一个函数指针对应的函数就会很方便. 打印裸指针(raw pointer)用 %p,%p除 ...

  4. 函数名地址、函数名取地址、函数名解引用问题

    以下,转载自http://blog.sina.com.cn/s/blog_6aafe9c90100xg2y.html 对一个函数进行 如下操作: 1.函数名地址 2.函数名取地址 3.函数名解引用 它 ...

  5. 吐槽程序员的变量名和函数名(给变量和函数取名字时遇到的坑)

    变量和函数在取名字的时候,一定要见名知意,不要用太宽泛的词! 能一看到函数名就知道这个函数是干嘛的,比写个注释看着舒服! 不要怕名字太长,宁愿名字长一点也比名字简短但又看不懂的名字要好! 名字清晰冗长 ...

  6. Linux系统程序运行时加载动态库路径顺序

    程序运行时加载动态库路径顺序(Linux) 在linux系统中,如果程序需要加载动态库,它会按照一定的顺序(优先级)去查找: 链接时路径(Link-time path)和运行时路径(Run-time ...

  7. linux直接运行程序加载动态库失败,扣丁学堂Linux培训详解程序运行时加载动态库失败解决方法...

    今天扣丁学堂Linux培训老师给大家介绍一下关于Linux程序运行时加载动态库失败的解决方法,希望对同学们学习有所帮助,下面我们一起来看一下吧. Linux下不能加载动态库问题 当出现下边异常情况 . ...

  8. python自定义函数名_使用自定义名称创建Python动态函数

    如果这个问题已经提出并得到了回答,我深表歉意. 我需要做的是非常简单的概念,但不幸的是,我还没有找到一个在线答案. 我需要在Python(Python2.7)中使用运行时的自定义名称创建动态函数.每个 ...

  9. android 函数名注册,Android JNI 函数注册的两种方式(静态注册/动态注册)

    在Android开发中,由于种种原因我们需要调用C/C++代码, 这个时候就要用到Android开发者都听说过的JNI(Java Native Interface)了, 在调用JNI相关方法之前, 要 ...

最新文章

  1. 网络国际治理系列 | WTO电子商务谈判合并文本数据跨境流动部分
  2. C# WinForm获取程序所在路径方法
  3. IAAS、PAAS与SAAS
  4. 记录一次服务进程强行退出的问题排查过程
  5. 阿里3篇技术论文入选国际顶级会议FAST2020,全球第一!
  6. 高性能MySQL(2)——Schema与数据类型的优化
  7. c#获取对象的唯一标识_DDD领域驱动设计实战 - 创建实体身份标识的常用策略
  8. 标榜 AI 的百度又玩区块链,跟风布局“加密猫”?
  9. python3-知识扩展扫盲易忘-generator的用法
  10. 如何在《救赎之路》中使用CPU粒子效果
  11. C语言-字符型数据与ASCII码表
  12. win7计算机怎么录屏,win7电脑怎么录屏,什么电脑录屏软件好用?
  13. 实现用python给微信指定联系人在指定时间发送消息(仅供学习)
  14. 华硕笔记本怎么禁用自带键盘
  15. GBIT51231-2016装配式混凝土结构建筑技术标准
  16. can总线短距离不用双绞线_CAN总线布线规范
  17. 微信大全 微信支付 微信登录 小程序 PC 公众号
  18. 0行代码,实现植物大战僵尸脚本
  19. 陀螺产业区块链第九季 | 如何用区块链搭建营销激励模型?
  20. 好看的充电宝有哪些?好看的充电宝推荐

热门文章

  1. OWASP TOP10漏洞分析和防御
  2. 简析JavaScript异步编程
  3. 什么软件能测试出内存有没有坏,有没有可以检测主板和内存坏没坏的软件
  4. SQL面试必会50题(含答案和学习链接)
  5. Python之ini配置文件详解
  6. 微型计算机主板usb电源损坏,硬盘盒也能带来灾难?修复主板烧毁的USB线路
  7. word中参考文献的引用方法
  8. 【oracle相关】ora-12520错误处理方法
  9. MySQL阶段_模块1_作业
  10. 尤大:怎么还生啃源码呢?我这就亲手给你写个丐版Vue