亲自动手~用VC++做DLL
一:Win32 Dynamic-Link Library 方式创建 Non-MFC DLL动态链接库
首先,打开VC++,选择File->New创建工程,使用Win32 Dynamic-Link Library方式,Project名为Win32Dll :
新建工程具有基本代码:
Finish之后得到新工程如下:
只有个简单的DllMain入口函数。
使用导出函数关键字_declspec(dllexport)创建DLL(对这个关键字的介绍参看下一遍博文吧)。因此,新生成Win32Dll.h文件,并在其中用关键字_declspec(dllexport)对要导出的函数进行声明。
在Win32Dll.cpp对其进行实现:
如上所示,我们想要导出的2个函数分别为JustSoSo和Max,他们都用关键字_declspec(dllexport)进行了修饰,但是不同的是JustSoSo还有用extern “C”进行修饰。有什么不同吗?extern “C”的修饰时必须的吗?我们具体来试验一下:
我们使用显示连接来调用这个DLL。
在工程文件中,首先要定义下我们需要从DLL中调用的函数的函数指针。
其中,FunctionFunc是JustSoSo的函数指针,而pMax是Max的函数指针。
然后就是进行调用:
对JustSoSo的调用:
调试结果,调用JustSoSo()成功!
对Max的调用:
调试结果,调用Max失败!原因是GetProcAddress返回的函数指针为0x00000000.
这是为什么呢?是extern “C”导致的吗?
在Win32Dll.h中修改Max的的声明为:
extern “C” _declspec(dllexport) int Max(int a,int b);
调试结果:调用Max成功!
那么,这个extern “C” 是个什么功效呢?如何理解这个状况呢?
答:在DLL的设计中,如果使用C++开发,通常在导出函数的定义中使用extern ”C“,为什么呢?其实是因为,当用户使用“运行时动态链接”的时候需要使用GetProcAddress函数来得到导出函数的地址,该函数是通过导出函数的函数名定位导出函数的,而C++编译器因为函数重载的原因会对开发者定义的函数名进行修饰,导致导出表中的函数名通常不是开发者使用的函数名,比如函数Max可能被修饰成??Max@QAEX.所以使用extern “C”通知编译器按照C的格式进行编译,而不是使用C++的方式进行编译。(使用VS提供的一个工具Dependency Walker可以查看DLL的导出函数)
我们试试使用隐式连接来调用这个DLL。
首先,将DLL和LIB文件放置于调用者同一目录下。
包含入lib文件:
方式一:
#pragma comment (lib,“Win32Dll.lib”)
方式二:
Project->setting->link->Object/library modules 添加 Win32Dll.lib
声明导出函数:
extern “C” _declspec(dllexport) int Max(int a,int b);
使用:
int temp = Max(5,8);
得到 temp 为 8,使用正确,调试成功~!
二:MFC AppWizard[dll]方式生成常规DLL
首先,打开VC++,选择File->New创建工程,选择MFC AppWizard[dll]方式,设置Project Name为MFCdll:
点击“OK”按钮,选择我们要生成的DLL类型:
如上图所示,对于“What type of DLL would you like to create?”提示,我们可以选择要创建的DLL为:
A.常规DLL静态链接到MFC
B.常规DLL动态链接到MFC
C.MFC扩展DLL
在此我们选择第二个选项。
不选择任何选项来回应“What features would you like in your DLL?”
然后点击“Finish”按钮。到此,我们的工程创建就完毕了。
现在我们想把一个add函数作为在DLL外部可以调用的导出函数,这次我们采用.def的方式来创建DLL。
那么首先在.def中田间add函数名:
然后,在类中添加add方法:
注意:在函数定义的起始需要此语句用来正确地切换MFC模块状态
创建的是动态DLL:
AFX_MANAGE_STATE(AfxGetAppModuleState());
创建的是静态DLL:
AFX_MANAGE_STATE(AfxGetStaticModuleState());
现在我们用显示连接来调用这个DLL:
先进行指针函数的定义,然后进行寻址等操作。
调试结果:失败了!!在dlladd(2,3)这一步!!这是为什么呢??
报错如下:
查询资料,有解决办法如下:
1.
要将导出函数声明为WINAPI,如:
void WINAPI AutoChess(BOOL, BOOL, BOOL);
使用时,函数指针的定义也需要注为WINAPI:
typedef void (WINAPI*AutoChess)(BOOL, BOOL, BOOL);
2.
是由于调用的接口与原接口参数不一致导致的,比如参数不符合或少参数输入导致.
但是,我按照提示进行修改声明及函数指针的定义
函数声明.h
函数指针定义:
依然是报错,这是为什么啊??
继续查询错误原因资料:
网络上搜索,出现此错误的原因如下:(和调用约定相关)
1、dll调用时,调用了dll中不存在的一个方法。出现此种情况,一般是在使用dll时没有把版本搞清楚。
2、由于调用的接口与原接口参数不一致导致的,比如参数不符合或少参数输入导致。这种错误方式比较常见
3、在dll中导出函数必须通过def文件来设定(__declspec(dllexport)这样的方式是为用.LIB连接准备的),且要声明为WINAPI,如:
void WINAPI AutoChess(char board[][15], char color, int &x, int &y);
4、Dll导出函数声明导出方法,与主模块中声明的导入方法不一致。使得调用时参数的传递中,破坏了调用堆栈,出现错误。
解决方法:请确定导出方(Dll等)与导入方(Exe等)的声明保持一致。
5、Dll导出函数本身破坏了调用堆栈。编码中最一般的错误比如:对象(如CString)等。
解决方法:保证产生的对象都被安全的释放。
进行调用的单步调试,发现确实是在传递调用参数时出现问题:
调用时,我传入的参数为整数2和3:
F11单步调试进入DLL函数中,传入的实参发生变化:
这是怎么回事呢?
参数类型和个数没有误差,那是哪里出问题了?聪明的你一定发现了,在DLL中我对all函数的声明进行了修改,但是定义没有修改,依然是:
这就是问题所在,定义的时候我依然让他带有"CMFCdll::”,因此,进行修改如下(或是 int add(int a ,int b)也可):
进行调试,完全正确~~!!!
关于WINAPI:
WINAPI 含义:
WINAPI见windef.h这个头文件
#define WINAPI __stdcall
默认情况下,我们的函数调用都是遵循__stdcall这个规则的。当然,也有诸如__cdecl、__pascal等规则。
使用__stdcall还是__cdecl或__pascal,在纯Windows编程下并非特别需要。
__stdcall:
1、进行函数调用,函数参数的入栈方式是最右边先入栈。
2、同时__stdcall规定,子函数负责栈的回收(调用者只负责压栈). 题外话:__pascal的调用规则是从左到右,正好与__stdcall相反。
3、C调用约定(即用__cdecl关键字说明)(The C default calling convention)按从右至左的顺序压参数入栈,由调用者把参数弹出栈。
MFC的缺省调用:
在函数调用过程中,会使用栈。__stdcall与__cdecl是两种不同的函数调用约定,定义了函数参数入栈的顺序,由调用函数还是被调用函数将参数弹出栈,以及产生函数修饰名的方法。
对于参数个数可变的函数,例如printf,使用的是__cdecl调用约定,Win32的API函数都遵循__stdcall调用约定。在VC++开发环境中,默认的编译选项是__cdecl,对于那些需要__stdcall调用约定的函数,在声明时必须显式地加上__stdcall。
DLL与WINAPI:
在一些地方windows要求必须使用winapi标准,比如说在dll中的输出函数(
制作dll 是为了 让其他的语言可以调用, 但是呢, 有的语言 如delphe 的参数调用方法就是 _stadcall……所以,为了dll 有更好的通用性, 一般 都用 WINAPI
)。
现在试试用隐式方式来调用DLL:
首先将DLL和LIB文件放到与调用DLL的EXE同级目录下。
在调用中引入lib库:
#pragma comment (lib,“MFCdll.lib”)
或者 Project->setting->link->Object/library modules 添加MFCdll.lib
声明导出函数:
extern int add(int a,int b);
使用:
int temp = add(2,3);
结果得出:temp为5,正确~!
亲自动手~用VC++做DLL相关推荐
- VC++动态链接库(DLL)编程深入浅出(zz)
1.概论 先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量.函数或类.在仓库的发展史上经历了" ...
- [转]C++学习:VC++动态链接库(DLL)编程深入浅出(zz)
转自:http://www.cnblogs.com/chio/archive/2007/11/03/948480.html 1.概论 先来阐述一下DLL(Dynamic Linkable Librar ...
- VC++动态链接库(DLL)编程深入浅出
深度好文作为入门理解非常不错 1.概论 先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量.函数或类.在 ...
- VC-基础:VC++动态链接库(DLL)编程深入浅出
1.概论 先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量.函数或类.在仓库的发展史上经历了" ...
- VC++动态链接库(DLL)编程(一)――理解库
VC++动态链接库(DLL)编程(一) ――理解库 作者:宋宝华 e-mail:21cnbao@21cn.com 1.概论 先来阐述一下DLL(Dynamic Linkable Library)的概 ...
- php get your hands dirty,BBC地道英语:To Get your Hands Dirty 亲自动手
Neil: Hello, I'm Neil. Li: What a miserable day it is today! Hey, I think that's Li! Neil: What on e ...
- php get your hands dirty,地道英语: To Get your Hands Dirty 亲自动手
The script of this programme 本节目台词 Neil: Hello, I'm Neil.Audio: Get your hands dirty 亲自动手 Li: What a ...
- VC++动态链接库(DLL)编程(四)――MFC扩展 DLL
VC++动态链接库(DLL)编程(四) ――MFC扩展 DLL 作者:宋宝华 e-mail:21cnbao@21cn.com 前文我们对非MFC DLL和MFC规则DLL进行了介绍,现在开始详细 ...
- Delphi 调用VC的DLL
Delphi 调用VC的DLL VC中DLL声名格式: Extern "C" void __declspec(dllexport) __stdcall ShowMess(HWND ...
最新文章
- 公共端接正极还是负极_【动力电池大事记】现代摩比斯启动首批模组工厂,松下拟挪威建厂,200名车主起诉现代,密歇根大学开发锂金属固态电池及“无负极”工艺...
- mysql性能调优与架构设计_了解架构设计远远不够!一文拆解 Tomcat 高并发原理与性能调优
- 浏览器时间久了重新登录_注意!今日开始打印一建准考证,附各地时间及常见问题汇总...
- 计算机二级报名可以报两个地方吗,同学们注意了!!这4个地区计算机二级还可以报名!!...
- POJ1269 直线相交
- Android开发之判断APP前后台的方法
- 北航 2012 秋季 现代软件工程 两人结对 作业要求
- QQ红包源码 大转盘抽奖源码下载 微信红包源码
- java面试编程面试题_完美的编程面试问题
- ROS仿真-记一次错误 gazebo-2 process has died exit code 2
- SCU 4437 Carries(二分乱搞)题解
- 现代语音信号处理之倒谱分析与同态滤波
- 普鸥知识产权|如何申请美国外观专利?申请费用、时间、流程?
- java gc 命令_Java 查看系统GC命令介绍
- springcloud整合openfeign启动报错,Error creating bean with name ‘feignTargeter‘
- Chrome手势插件
- 个人第1次作业:阅读与准备作业
- Linux的memory日志,Linux:日志,cpu,memory,mount,load等系统信息查看
- 详细浮点型数据的存储讲解
- python实现kd树以及最近邻查找算法