ImageVerifierCode 换一换
格式:DOCX , 页数:19 ,大小:872.46KB ,
资源ID:158153      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bingdoc.com/d-158153.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(动态连接库.docx)为本站会员(b****1)主动上传,冰点文库仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰点文库(发送邮件至service@bingdoc.com或直接QQ联系客服),我们立即给予删除!

动态连接库.docx

1、动态连接库动态链接库(DLL)一、相关概念动态链接库(Dynamic Link Library):动态链接库通常都不能直接运行,也不能接收消息。它们是一些独立的文件,文件后缀一般为.DLL,其中包含供其他可执行程序或其它DLL调用的函数。只有在其它模块调用动态链接库中的函数时,它才发挥作用。例如,Windows API中的所有函数都包含在DLL中。其中有3个最重要的DLL,Kernel32.dll,它包含用于管理内存、进程和线程的各个函数;User32.dll,它包含用于执行用户界面任务(如窗口的创建和消息的传送)的各个函数;GDI32.dll,它包含用于画图和显示文本的各个函数。他们一般位于

2、C:windowsSystem32或类似的目录下。通俗一点说,动态链接库就是将很多函数放到一起形成一个集合模块,注册后供其他应用程序运行时动态调用。这许许多多的函数又可分为内部函数和导出函数。内部函数是用来在动态链接库内部调用的函数,主要用来实现动态链接库的实际功能;导出函数,顾名思义就是供外部模块或者应用程序在运行的时候调用的,是应用程序和动态链接库间的接口。导出函数包含在导出表中, 导出表包含动态链接库中所有可以被外部调用的函数名(对外的接口)。 顾名思义,动态链接库,是动态链接,它是相对于静态链接而言的。静态库:函数和数据被编译进一个二进制文件(通常扩展名为.LIB)。在其他程序使用静态

3、库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据,并把它们和应用程序的其它模块组合起来创建最终的可执行文件(.EXE文件)。发布产品时,只需发布.EXE文件即可,不需要发布.LIB文件。优点:无须包括函数库所包含的函数代码,应用程序可以利用标准的函数集;缺点:两个应用程序运行时同时使用同一静态链接库中的函数,需要使用同一函数代码的两份拷贝,降低内存使用率。动态库:在其他程序使用动态库的时候,往往要用到DLL提供者提供的两个文件:一个引入库(.LIB)和一个DLL(.DLL)。但这里的.LIB文件和静态库中的.LIB文件有着本质的差别,这里的.LIB只包含导出的函数、变量的符号

4、名,.DLL包含实际的函数和数据。在编译链接时,只需要链接.LIB,DLL中的函数代码和数据并不复制到可执行文件中,在运行的时候,才去加载DLL,并访问DLL中的导出函数。优点:可以采用多种编程语言来编写;提供二次开发的平台;简化项目管理;可以节省磁盘空间和内存,等。动态链接库DLL和可执行文件EXE的区别:可执行文件运行起来后有自己的独立的进程空间,而动态链接库的导出函数被动态链接到应用程序的进程空间,这样多个应用程序可以共享一份代码副本。另外,动态链接库可以包括一个导出表,记录该动态链接库对外提供的函数接口。二、DLL的开发、声明和调用1、Win32 DLL和MFC DLL的编写。 见下面

5、的例子。2、DLL中声明导出函数的三种方式:使用_declspec(dllexport) 、使用extern、使用.def文件3、导出整个类或仅导出类中的部分成员函数。导出整个类:在类定义的关键字class后面加_declspec(dllexport)导出成员函数:在成员函数定义的最前面加_declspec(dllexport)4、调用动态链接库的两种方式:隐式链接:需在工程设置中添加对.lib的引用或者使用#pragma comment ( lib, Dll1.lib ) 显式加载:需要调用LoadLibrary()或者类似的函数加载动态链接库,再使用GetProcessAddress()获

6、得要调用的每个函数的函数指针,使用完毕后,调用FreeLibrary()卸载DLL。这两种加载方式都需要预先声明外部函数的类型,然后才能调用成功。只是声明的方式有所不同。三、实例1:创建一个Win32 DLL工程Dll11.1 使用_declspec(dllexport)声明导出函数在Dll1.cpp中输入以下代码:#include #include int AbsSub(int a, int b) /注意,前面没有加 _declspec(dllexport) 导出声明 return (a b ? a - b : b - a);_declspec(dllexport) int Add(int

7、a, int b) /需要导出的函数名前面要加导出声明 return a + b;_declspec(dllexport) int Sub(int a, int b) return a - b;_declspec(dllexport) int Sub(int a, int b, bool bAbs) if (bAbs) return AbsSub(a, b); else return Sub(a, b);class /*_declspec(dllexport)*/ CMyPointpublic: void Print(int x, int y) HWND hWnd = :GetForegrou

8、ndWindow(); HDC hdc = :GetDC(hWnd); char buf20; memset(buf, 0, 20); sprintf(buf, x = %d, y = %d, x, y); :TextOut(hdc, 10, 10, buf, strlen(buf); :ReleaseDC(hWnd, hdc); ; void Show(int x, int y) HWND hWnd = :GetForegroundWindow(); HDC hdc = :GetDC(hWnd); char buf20; memset(buf, 0, 20); sprintf(buf, x

9、= %d, y = %d, x, y); :TextOut(hdc, 10, 30, buf, strlen(buf); /仅仅y坐标不同。 :ReleaseDC(hWnd, hdc); ;class CPersonpublic: /*_declspec(dllexport)*/ void ShowName(const char * name) HWND hWnd = :GetForegroundWindow(); HDC hdc = :GetDC(hWnd); :TextOut(hdc, 10, 50, name, strlen(name); :ReleaseDC(hWnd, hdc); ;

10、编译链接后在debug目录下可以找到dll1.lib和dll1.dll两个文件。注意,如果代码中每个函数名前都没有声明_declspec(dllexport),编译时将不会生产dll1.lib文件。这样的DLL将没有使用价值。这里有一个问题,调用这个.DLL的其他程序如何知道其中的函数名称和用法呢?答案是:在DOS下使用DLL导出函数查看命令dumpbin /exports dll1.dll,就可以看到dll1.dll文件包含了上述2个Add和Sub的信息。Dumpbin.exe文件一般位于 C:Program FilesMicrosoft Visual StudioVC98Bin目录下。如果

11、在任意目录不能执行Dumpbin,则需要先运行C:Program FilesMicrosoft Visual StudioVC98BinVCVARS32.BAT,使系统将该路径设置为缺省路径。Dumpbin查看结果如下: ordinal hint RVA name 1 0 00001005 ?AddYAHHHZ 2 1 0000100A ?SubYAHHHZ 3 2 00001014 ?SubYAHHH_NZ 其中RVA列包含的数值表示导出函数在DLL模块中的偏移地址,通过该地址可以在模块中找到该函数。Name列是导出函数的名称,但我们发现函数名字被改编了。但其中没有AbsSub的信息,这是因

12、为我们没有在AbsSub函数名前加入导出声明_declspec(dllexport)。因此,在要导出的函数名前面一定要加上导出声明。1.2 使用extern “C” _declspec(dllexport)解决函数名改编问题 加上extern “C”之后,用C+写的DLL就可以在纯C环境使用了,但写DLL时就不能使用类了,因为C语言不支持类。因此,除非特殊需求,一般不使用这种方式。使用dumpbin命令查看,发现函数名字没有被改编。1.3 使用.def文件导出函数声明解决函数名改编问题(见后面的MFC DLL实例).def是由一个或者多个用于描述DLL属性的文本文件。它包含以下一些模块定义语句

13、:LIBRARY:指出动态链接库的名字,链接器负责将该名字放到动态链接库中;DESCRIPTION:描述该动态链接库的用途;EXPORTS:指出被导出函数的名称和序号;例如:LIBRARY DllADESCRIPTION “实现数据库的操作”EXPORTSAdd 1Delete 2Member 3 对于使用MFC 应用程序向导(AppWizard)生成的动态链接库,应用程序向导(AppWizard)会自动生成一个.def文件;对于非MFC DLL,可手工添加该文件到工程中。使用dumpbin命令查看,发现函数名字没有被改编。2、以隐式加载的方式使用DLL2.1 以_declspec(dllim

14、port)方式声明外部函数创建一个MFC对话框工程DllTest来调用上述DLL中的3个函数。加入3个按钮,并添加代码。如下:_declspec(dllimport) int Add(int a, int b); /使用_declspec(dllimport) 声明DLL函数_declspec(dllimport) int Sub(int a, int b); /注意:是dllimport,不是dllexport_declspec(dllimport) int Sub(int a, int b, bool bIsAbs);void CDllTestDlg:OnBtnAdd() CString

15、str; str.Format(3 + 4 = %d, Add(3, 4); MessageBox(str);void CDllTestDlg:OnBtnSub() CString str; str.Format(3 - 4 = %d, Sub(3, 4); MessageBox(str);void CDllTestDlg:OnBtnAbssub() CString str; str.Format(abs(3 - 4) = %d, Sub(3, 4, true); MessageBox(str);编译发现,出现LNK2001链接错误。将dll1.lib复制到当前工程目录下,并做如下工程设置:

16、再次编译将会通过。但运行却又出现错误,提示找不到dll1.dll文件。将dll1.dll也复制到当前工程目录下,再次运行后,一切就正常了。从上面的编译链接和运行的过程可以知道:dll1.lib文件是链接器进行链接时才需要的,它只是提供了dll1.dll文件中导出函数的函数名等信息,并没有包含实际的函数和数据。函数的实际功能是在程序运行时动态的调用dll1.dll文件时才加载的。也就是说,DLL的调用者在开发时需要.lib文件,发行时需要.dll文件。 也可以不用在工程中进行dll1.lib的设置,在需要调用DLL函数的代码的前面,添加如下语句:#pragma comment ( lib, Dl

17、l1.lib ) 这和上图中在工程设置里写上Dll1.lib的效果一样。 使用Viaual Studio提供Depends工具可以查看一个EXE文件或者DLL文件在运行时所依赖的所有动态链接库。如下图所示,说明dlltest.exe文件需要dll1.dll,mfc42d.dll等的支持。2.2 以extern方式声明外部函数上面是使用_declspec(dllimport)对外部函数进行的声明,它明确告诉编译器,函数是来自于DLL的,也是较好的声明方式。还有一种extern声明外部函数的方式,将上述dll1.cpp文件的声明中的_declspec(dllimport)替换成extern即可,运

18、行效果也是一样的。如下:extern int Add(int a, int b); /使用extern声明DLL中的函数extern int Sub(int a, int b);extern int Sub(int a, int b, bool bIsAbs);2.3 使用头文件完善函数的声明,明确告知使用者函数的调用方式。上面讲过,可以使用DOS命令dumpbin来查看DLL的导出函数有哪些,但dumpbin命令看不到函数的具体参数,返回类型等具体的函数原型。因此,作为DLL的开发者,有必要提供完整的导出函数的原型,明确告知调用者DLL的用法。为Dll1工程添加Dll1.h头文件,代码如下:

19、#ifndef DLL1_API #define DLL1_API _declspec(dllimport)#endif/int AbsSub(int a, int b) /实际工作中删掉这一行而不是注释掉,隐藏设计创意DLL1_API int Add(int a, int b);DLL1_API int Sub(int a, int b);DLL1_API int Sub(int a, int b, bool bAbs);在Dll1.cpp文件的最前面加上对DLL1_API的定义,如下:/在此对DLL1_API进行声明,表示当前是导出模式。/而本DLL的最终使用者不需进行任何声明,默认使用导

20、入模式。#define DLL1_API _declspec(dllexport)将Dll1.h文件复制到DllTest工程的目录下,包含进工程的.cpp文件中去,并注释掉原有的_declspec(dllexport)声明。这样,同一个头文件就可以被开发者和使用者共同使用了。3、以显式加载的方式使用DLL这种方式需要调用LoadLibrary()或者类似的函数,来加载动态链接库,再使用GetProcessAddress()获得要调用的每个函数的函数指针,使用完毕后,调用FreeLibrary()卸载DLL。这种方式不需要*.lib文件,也不需要包含*.h文件。但需要知道被调用函数的原型,以便为

21、GetProcessAddress()的返回值定义相应的函数指针。3.1 建立一个MFC DLL工程MyDll3.2 在MyDll.cpp文件中添加函数声明#include MyDll.h#ifdef _DEBUG#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE = _FILE_;#endifvoid MyFun1(CWnd *h);void MyFun2(CWnd *h);添加函数实现:CMyDllApp theApp;void MyFun1(CWnd *h) CBrush newBrush; newBrush.Create

22、SolidBrush(RGB(0,0,255); CBrush *pOldBrush; CClientDC dc(h); pOldBrush = dc.SelectObject(&newBrush); dc.Ellipse(50,30,300,200); dc.SelectObject(pOldBrush);void MyFun2(CWnd *h) CPen newPen; CPen *pOldPen; newPen.CreatePen(PS_SOLID,1,RGB(0,0,255); CClientDC dc(h); CRect rect(40,20,310,210); Invalidate

23、Rect(HWND_*)h,&rect,TRUE); pOldPen = dc.SelectObject(&newPen); dc.Ellipse(50,30,300,200); dc.SelectObject(pOldPen);3.3 .def文件的编写:; MyDll.def : Declares the module parameters for the DLL.LIBRARY MyDllDESCRIPTION MyDll Windows Dynamic Link LibraryEXPORTS ; Explicit exports can go here MyFun1 MyFun2注意:

24、.def文件注释是使用分号;而不是/ 3.4 编译生成MyDll.Dll文件将生成的MyDll.dll文件拷贝到D:WINDOWSsystem32目录下.这一部叫做动态链接库文件的注册.注意:Debug和Release编译方式生成的该文件位置不一样。怎么找? 3.5 使用动态链接库1建立使用动态链接库的工程(顺便介绍MFC DLL的使用)其他步骤默认既可。涉及如下对话框界面添加代码:1声明全局变量在TesDllDlg.cpp中添加如下代码:HINSTANCE hDLL = NULL; / 声明全局变量hDLL用于存放DLL的句柄并初始化为空typedef void (* MYFUN1)( CW

25、nd *h);/ 声明函数指针类型,用它来声明变量MyFun1;MYFUN1 MyFun1;typedef void (* MYFUN2)( CWnd *h);MYFUN2 MyFun2;如图:2 添加按钮点击消息响应函数void CTestDllDlg:OnLoadDll() / TODO: Add your control notification handler code here if(hDLL != NULL) MessageBox(你已经装载了MyDll.DLL文件!); return; hDLL=LoadLibrary(MyDll.DLL); if(hDLL = NULL) Me

26、ssageBox(无法装载MyDll.DLL文件!); return; MyFun1 = (MYFUN1)GetProcAddress(hDLL,MyFun1); MyFun2 = (MYFUN2)GetProcAddress(hDLL,MyFun2);void CTestDllDlg:OnRunDll() / TODO: Add your control notification handler code here if(hDLL = NULL) MessageBox(你还没有装载MyDll.DLL文件!); return; for(int i=0;i=10;i+) MyFun1(this)

27、; Sleep(1000); MyFun2(this); Sleep(1000); 程序运行结果:反复绘制实心的椭圆和空心的椭圆,如下图:需要特别注意的是,如果函数名字被改编了,在使用GetProcessAddress()时,其第二个参数需要使用改编后的函数名。另外,使用DEF导出函数时,不允许进行函数重载,但使用_declspec(dllexport)导出时就可以,其原因是_declspec(dllexport)对函数进行了名称改编。另外也可根据导出函数的序号来获取函数指针。但不推荐使用这种方法。如:MYFUN2 Draw2 = (MYFUN2)GetProcAddress(hDLL, MA

28、KEINTRESOURCE(2);和MYFUN2 Draw2 = (MYFUN2)GetProcAddress(hDLL, “MyFun2”);是一样的效果。四、DllMain 可有可无。如有,其中应尽量少的写代码。可以在此做一些初始化的工作。BOOL WINAPI DllMain( HINSTANCE hinstDLL, / handle to DLL module DWORD fdwReason, / reason for calling function LPVOID lpvReserved / reserved);作业:1、 提交: 编写一个Win32 DLL工程MaxMin,对外提供带2个参数和3个参数的求最大值和最小值的4个导出函数:int Max(int a, int b, int c); int Max(int a, int b); int Min(int a, int b, int c); int Min(int a, int b); 并另外编写一个MFC对话框工程TestMaxMin,使用隐式链接方式对MaxMin.dll进行测试。2、 思考: MFC DLL中能导出某个类中的所有函数吗?如果能,有哪些限制?为什么?如何导出?使用上述Dll1工程中的例子进行测试。(考点: DEF和declspec(dllexport)两种导出方式的比较及混合使用)

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2