亲自动手~用VC++做DLL

原文地址:亲自动手~用VC++做DLL 作者:__one_day__

一: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)对要导出的函数进行声明。

正确h

在Win32Dll.cpp对其进行实现:

正确cpp

如上所示,我们想要导出的2个函数分别为JustSoSo和Max,他们都用关键字_declspec(dllexport)进行了修饰,但是不同的是JustSoSo还有用extern “C”进行修饰。有什么不同吗?extern “C”的修饰时必须的吗?我们具体来试验一下:

   我们使用显示连接来调用这个DLL

   在工程文件中,首先要定义下我们需要从DLL中调用的函数的函数指针。

 调用正确define

其中,FunctionFunc是JustSoSo的函数指针,而pMax是Max的函数指针。

   然后就是进行调用:

对JustSoSo的调用:

  调用正确1

调试结果,调用JustSoSo()成功!

对Max的调用:

调用正确2

调试结果,调用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:

1

点击“OK”按钮,选择我们要生成的DLL类型:

2

如上图所示,对于“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函数名:

3

然后,在类中添加add方法:

4

5

注意:在函数定义的起始需要此语句用来正确地切换MFC模块状态

创建的是动态DLL:

AFX_MANAGE_STATE(AfxGetAppModuleState());
创建的是静态DLL:

AFX_MANAGE_STATE(AfxGetStaticModuleState());

现在我们用显示连接来调用这个DLL

先进行指针函数的定义,然后进行寻址等操作。

6
调试结果:失败了!!在dlladd(2,3)这一步!!这是为什么呢??

报错如下:

7

查询资料,有解决办法如下:

1.
要将导出函数声明为WINAPI,如:
void WINAPI AutoChess(BOOL, BOOL, BOOL);

使用时,函数指针的定义也需要注为WINAPI: 

typedef void (WINAPI*AutoChess)(BOOL, BOOL, BOOL);

2.
是由于调用的接口与原接口参数不一致导致的,比如参数不符合或少参数输入导致.

但是,我按照提示进行修改声明及函数指针的定义

函数声明.h

3

函数指针定义:

5

依然是报错,这是为什么啊??

继续查询错误原因资料:

网络上搜索,出现此错误的原因如下:(和调用约定相关)

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:

1

F11单步调试进入DLL函数中,传入的实参发生变化:

2

这是怎么回事呢?

参数类型和个数没有误差,那是哪里出问题了?聪明的你一定发现了,在DLL中我对all函数的声明进行了修改,但是定义没有修改,依然是:

4

这就是问题所在,定义的时候我依然让他带有"CMFCdll::”,因此,进行修改如下(或是 int add(int a ,int b)也可):

6

进行调试,完全正确~~!!!

 

关于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,正确~!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值