动态链接库编程
- 格式:pdf
- 大小:1.08 MB
- 文档页数:64
第4节我们对非MFC DLL进行了介绍,这一节将详细地讲述MFC规则DLL的创建与使用技巧。
另外,自从本文开始连载后,收到了一些读者的e-mail。
有的读者提出了一些问题,笔者将在本文的最后一次连载中选取其中的典型问题进行解答。
由于时间的关系,对于读者朋友的来信,笔者暂时不能一一回复,还望海涵!由于笔者的水平有限,文中难免有错误和纰漏,也热诚欢迎读者朋友不吝指正!MFC规则DLL的概念体现在两方面:(1)它是MFC的“是MFC的”意味着可以在这种DLL的内部使用MFC;(2)它是规则的“是规则的”意味着它不同于MFC扩展DLL,在MFC规则DLL的内部虽然可以使用MFC,但是其与应用程序的接口不能是MFC。
而MFC扩展DLL与应用程序的接口可以是MFC,可以从MFC扩展DLL中导出一个MFC类的派生类。
Regular DLL能够被所有支持DLL技术的语言所编写的应用程序调用,当然也包括使用MFC的应用程序。
在这种动态连接库中,包含一个从CWinApp继承下来的类,DllMain 函数则由MFC自动提供。
Regular DLL分为两类:(1)静态链接到MFC 的规则DLL静态链接到MFC的规则DLL与MFC库(包括MFC扩展DLL)静态链接,将MFC 库的代码直接生成在.dll文件中。
在调用这种DLL的接口时,MFC使用DLL的资源。
因此,在静态链接到MFC 的规则DLL中不需要进行模块状态的切换。
使用这种方法生成的规则DLL其程序较大,也可能包含重复的代码。
(2)动态链接到MFC 的规则DLL动态链接到MFC 的规则DLL 可以和使用它的可执行文件同时动态链接到MFC DLL 和任何MFC扩展DLL。
在使用了MFC共享库的时候,默认情况下,MFC使用主应用程序的资源句柄来加载资源模板。
这样,当DLL和应用程序中存在相同ID的资源时(即所谓的资源重复问题),系统可能不能获得正确的资源。
因此,对于共享MFC DLL的规则DLL,我们必须进行模块切换以使得MFC能够找到正确的资源模板。
C#中创建和使⽤动态链接库动态链接库英⽂为DLL,是Dynamic Link Library 的缩写形式,DLL是⼀个包含可由多个程序同时使⽤的代码和数据的库,它有助于共享数据和资源。
(⼀)创建动态链接库1)新建⼀个类库2)编写该类库例如:1: using System;2: using System.Collections.Generic;3: using System.Linq;4: using System.Text;5:6: namespace DllTest7: {8: public class MyClass9: {10: public void ShowMessage()11: {12: Console.WriteLine("你已成功调⽤了动态链接!");13: Console.ReadLine();14: }15: }16: }3)⽣成动态链接库(⼆)使⽤动态链接库1)添加引⽤右击项⽬-添加引⽤-浏览找到本地的dll⽂件2)using 该dll⽂件⾥⾯代码的名称空间例如:1: using System;2: using System.Collections.Generic;3: using System.Linq;4: using System.Text;5:6: //using 要引⽤的dll⽂件的命名空间7: using DllTest;8:9: namespace DllExample10: {11: class Program12: {13: static void Main(string[] args)14: {15: DllTest.MyClass myTest = new DllTest.MyClass();16: myTest.ShowMessage();17: }18: }19: }。
VC++动态链接库(DLL)编程(一)――理解库1.概论先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL 看成一种仓库,它提供给你一些可以直接拿来用的变量、函数或类。
在仓库的发展史上经历了“无库-静态链接库-动态链接库”的时代。
静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib中的指令都被直接包含在最终生成的EXE文件中了。
但是若使用DLL,该DLL不必被包含在最终EXE文件中,EXE文件执行时可以“动态”地引用和卸载这个与EXE独立的DLL文件。
静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。
对动态链接库,我们还需建立如下概念:(1)DLL的编制与具体的编程语言及编译器无关只要遵循约定的DLL接口规范和调用方式,用各种语言编写的DLL都可以相互调用。
譬如Windows提供的系统DLL(其中包括了Windows的API),在任何开发环境中都能被调用,不在乎其是Visual Basic、Visual C++还是Delphi。
(2)动态链接库随处可见我们在Windows目录下的system32文件夹中会看到kernel32.dll、user32.dll和gdi32.dll,windows的大多数API都包含在这些DLL中。
kernel32.dll中的函数主要处理内存管理和进程调度;user32.dll中的函数主要控制用户界面;gdi32.dll中的函数则负责图形方面的操作。
一般的程序员都用过类似MessageBox的函数,其实它就包含在user32.dll这个动态链接库中。
由此可见DLL对我们来说其实并不陌生。
(3)VC动态链接库的分类Visual C++支持三种DLL,它们分别是Non-MFC DLL(非MFC动态库)、MFC Regular DLL(MFC规则DLL)、MFC Extension DLL(MFC扩展DLL)。
Windows Dynamic-Link Library1.概论先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量、函数或类。
在仓库的发展史上经历了“无库-静态链接库-动态链接库”的时代。
静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib中的指令都被直接包含在最终生成的EXE文件中了。
但是若使用DLL,该DLL不必被包含在最终EXE文件中,EXE文件执行时可以“动态”地引用和卸载这个与EXE独立的DLL文件。
静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。
对动态链接库,我们还需建立如下概念:(1)DLL 的编制与具体的编程语言及编译器无关只要遵循约定的DLL接口规范和调用方式,用各种语言编写的DLL都可以相互调用。
譬如Windows提供的系统DLL(其中包括了Windows的API),在任何开发环境中都能被调用,不在乎其是Visual Basic、Visual C++还是Delphi。
(2)动态链接库随处可见我们在Windows目录下的system32文件夹中会看到kernel32.dll、user32.dll和gdi32.dll,windows的大多数API都包含在这些DLL 中。
kernel32.dll中的函数主要处理内存管理和进程调度;user32.dll中的函数主要控制用户界面;gdi32.dll中的函数则负责图形方面的操作。
一般的程序员都用过类似MessageBox的函数,其实它就包含在user32.dll这个动态链接库中。
由此可见DLL对我们来说其实并不陌生。
(3)VC动态链接库的分类Visual C++支持三种DLL,它们分别是Non-MFC DLL(非MFC动态库)、MFC Regular DLL(MFC规则DLL)、MFC Extension DLL (MFC扩展DLL)。
C语言动态链接库编程VB6中引用VC6编写的.DLL函数进行屏幕绘图一、动态链接库的基本概念动态链接库(Dynamic Link Library,简称DLL)是由函数对象以及资源组成的二进制库文件,此库文件能同时被多个进程或者应用程序使用。
图1、VB6中使用.dll库函数在窗体中绘制简单飞机模型上图1展现了在VB6中调用VC6建立的屏幕绘图动态链接库(.DLL)的应用效果,程序的运行非常顺畅;实现了在VB中绘图象在VC中一样的效果(3D+消隐+光照)。
二、动态链接库不可少的两个文件1. 创建动态库的函数源文件(.cpp):编写库函数源码的源文件。
2. 导出函数名字的文件(.def):编写Lib(*.dll)入口函数名字的源文件。
三、编程创建动态链接库下面以简单的屏幕绘图函数为例,实现用VC6创建动态链接库,在VB6中调用(.dll)函数的全过程。
1. 打开VC6:文件-新建-工程(空工程)图2、新建Win32 DLL工程2. 添加(.cpp)文件到工程(Graph.cpp)图3、新建Graph.cpp文件并添加到工程3. 添加(.def)文件到工程(Graph.def)图4、新建Graph.def文件并添加到工程4.在Graph.cpp文件中编写函数源码#ifndef WIN32GDI_API#define WIN32GDI_API _declspec(dllexport)#define EXC extern "C"#define CALL _stdcall#endif#include <windows.h>#include <stdio.h>#include <math.h>typedef struct _tagSIZE2I{int width;int height;}SIZE2I;typedef struct _tagCOLOR4U{unsigned char b;unsigned char g;unsigned char r;unsigned char a;}COLOR4U;typedef struct _tagCOLOR4I{int b;int g;int r;int a;}COLOR4I;HDC _g_hDC;COLOR4U *_g_bitmap;COLOR4I _g_ftcolor;COLOR4I _g_bkcolor;SIZE2I _g_size={300,200};SIZE2I _g_oldsize={-100,-100};int _g_win_test=0;EXC WIN32GDI_API void CALL SetGraphicsDC(HDC shdc){_g_hDC=shdc;}EXC WIN32GDI_API void CALL InitGraph(int w,int h){if(w<200)w=200;if(h<180)h=180;_g_size.width=w;_g_size.height=h;if(_g_win_test==1){if(_g_size.width!=_g_oldsize.width||_g_size.height!=_g_oldsize.height){ _g_oldsize=_g_size;delete []_g_bitmap;_g_bitmap=new COLOR4U [_g_size.width*_g_size.height];}}if(_g_win_test==0){_g_oldsize=_g_size;_g_bitmap=new COLOR4U [_g_size.width*_g_size.height];_g_win_test=1;}}int TestNumber(int number){if(number>255) number=255;if(number<0) number=0;return number;}COLOR4I GetColor4i(int r,int g,int b,int a){COLOR4I cr={b,g,r,a};return cr;}EXC WIN32GDI_API void CALL SetColor(int r,int g,int b){r=TestNumber(r);g=TestNumber(g);b=TestNumber(b);_g_ftcolor=GetColor4i(r,g,b,0);}EXC WIN32GDI_API void CALL SetBackColor(int r,int g,int b){r=TestNumber(r);g=TestNumber(g);b=TestNumber(b);_g_bkcolor=GetColor4i(r,g,b,0);}EXC WIN32GDI_API void CALL Flush(){HBITMAP _cgl_bitmap=CreateBitmap(_g_size.width,_g_size.height,1,32,_g_bitmap);HBRUSH hb=CreatePatternBrush(_cgl_bitmap);SelectObject(_g_hDC,hb);Rectangle(_g_hDC,10,10,_g_size.width-25,_g_size.height-50);DeleteObject(hb);DeleteObject(_cgl_bitmap);}COLOR4U GetColor4u(COLOR4I ci){COLOR4U ti={ci.b,ci.g,ci.r,ci.a};return ti;}EXC WIN32GDI_API void CALL Clear(){int len,i;COLOR4U tm=GetColor4u(_g_bkcolor);len=_g_size.width*_g_size.height;for(i=0;i<len;i++){_g_bitmap[i]=tm;}}EXC WIN32GDI_API void CALL SetPixel2i(int x,int y){COLOR4U tm;tm=GetColor4u(_g_ftcolor);if(x>=0&&x<_g_size.width-1&&y>=0&&y<_g_size.height-1){int len=y*_g_size.width+x;_g_bitmap[len]=tm;}}EXC WIN32GDI_API void CALL Line2Di (int x1,int y1,int x2,int y2){int i,len;float dx,dy,x,y;x=(float)x1;y=(float)y1;if(abs(x2-x1)>=abs(y2-y1))len=abs(x2-x1);elselen=abs(y2-y1);SetPixel2i((int)x,(int)y);dx=(float)(x2-x1)/len;dy=(float)(y2-y1)/len;for(i=1;i<=len;i++){x+=dx;y+=dy;SetPixel2i((int)x,(int)y);}}5. 在Graph.def文件中编写Lib(*.dll)入口函数名字的源文件LIBRARY GraphEXPORTSSetGraphicsDCInitGraphSetColorSetBackColorFlushClearSetPixel2iLine2Di6. 编译动态链接库文件图5、成功编译Graph.dll7. 把生成的Graph.dll文件拷贝到C:\Windows\System中(Win7系统)8. 打开VB6:新建工程-标准EXE图6、VB工程源码屏幕截图9. 运行工程图7、VB工程执行结果屏幕截图至此就完成了从编写动态链接库到调用库的全过程。
您正在看的VC教程是:关于VC中的DLL的编程。
在我们实际用软件时,经常可看到许多动态连接库。
动态连接库有其自身的优点如节省内存、支持多语种等功能,而且,当DLL中的函数改变后,只要不是参数的改变调用起的函数并不需要重新编译。
这在编程时十分有用。
至于其他妙处,各位在电脑杂志、书籍中都能看到,我这里再说就是废话了.这次小弟我所要讲的是如何在VC5.0中如何做自己的Win32 DLLs,各位要做自己的动态连接库,首先要知道DLL在VC5.0中都有哪几种分类。
VC支持三种DLL,它们是:1.Non-MFC Dlls2.Regular Dlls3.Extension Dlls Note:翻译措辞不当,故遇到术语是引用原词Non-MFC DLL:指的是不用MFC的类库结构,直接用C语言写的DLL,其输出的函数一般用的是标准C接口,并能被非MFC或MFC编写的应用程序所调用。
LL,Regular DLL:和下述的Extension Dlls一样,是用MFC类库编写的。
明显的特点是在源文件里有一个继承CWinApp的类。
其又可细分成静态连接到MFC和动态连接到MFC 上的。
但静态连接到MFC的动态连接库只被VC的专业般和企业版所支持。
Extension DLL:用来实现从MFC所继承下来的类的重新利用,也就是说,用这种类型的动态连接库,可以用来输出一个从MFC所继承下来的类。
Extension DLL使用MFC 的动态连接版本所创建的,并且它只被用MFC类库所编写的应用程序所调用。
各位看到这里如果眼有点花或头有点晕,请别泄气,再看两遍,然后继续往下看,定有收获。
标题: 关于VC中的DLL的编程[1]这一节介绍Non-MFC DLLs的编写方法。
下面是一个通用的写法:BOOL APIENTRY DllMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved){switch( ul_reason_for_call ) {case DLL_PROCESS_ATTACH:.......case DLL_THREAD_ATTACH:.......case DLL_THREAD_DETACH:.......case DLL_PROCESS_DETACH:.......}return TRUE;}每一个DLL必须有一个入口点,这就象我们用C编写的应用程序一样,必须有一个WINMAIN函数一样。
Win32环境下动态链接库(DLL)编程原理比较大应用程序都由很多模块组成,这些模块分别完成相对独立的功能,它们彼此协作来完成整个软件系统的工作。
其中可能存在一些模块的功能较为通用,在构造其它软件系统时仍会被使用。
在构造软件系统时,如果将所有模块的源代码都静态编译到整个应用程序EXE文件中,会产生一些问题:一个缺点是增加了应用程序的大小,它会占用更多的磁盘空间,程序运行时也会消耗较大的内存空间,造成系统资源的浪费;另一个缺点是,在编写大的EXE 程序时,在每次修改重建时都必须调整编译所有源代码,增加了编译过程的复杂性,也不利于阶段性的单元测试。
Windows系统平台上提供了一种完全不同的较有效的编程和运行环境,你可以将独立的程序模块创建为较小的DLL(Dynamic Linkable Library)文件,并可对它们单独编译和测试.在运行时,只有当EXE程序确实要调用这些DLL模块的情况下,系统才会将它们装载到内存空间中.这种方式不仅减少了EXE文件的大小和对内存空间的需求,而且使这些DLL模块可以同时被多个应用程序使用。
Microsoft Windows自己就将一些主要的系统功能以DLL模块的形式实现。
例如IE中的一些基本功能就是由DLL文件实现的,它可以被其它应用程序调用和集成。
一般来说,DLL是一种磁盘文件(通常带有DLL扩展名),它由全局数据、服务函数和资源组成,在运行时被系统加载到进程的虚拟空间中,成为调用进程的一部分。
如果与其它DLL之间没有冲突,该文件通常映射到进程虚拟空间的同一地址上。
DLL模块中包含各种导出函数,用于向外界提供服务.Windows 在加载DLL模块时将进程函数调用与DLL文件的导出函数相匹配。
在Win32环境中,每个进程都复制了自己的读/写全局变量。
如果想要与其它进程共享内存,必须使用内存映射文件或者声明一个共享数据段。
DLL模块需要的堆栈内存都是从运行进程的堆栈中分配出来的。
VC++动态链接库(DLL)编程――理解库作者:宋宝华e-mail:21cnbao@1.概论先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL 看成一种仓库,它提供给你一些可以直接拿来用的变量、函数或类。
在仓库的发展史上经历了“无库-静态链接库-动态链接库”的时代。
静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib中的指令都被直接包含在最终生成的EXE文件中了。
但是若使用DLL,该DLL不必被包含在最终EXE文件中,EXE文件执行时可以“动态”地引用和卸载这个与EXE 独立的DLL文件。
静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。
对动态链接库,我们还需建立如下概念:(1)DLL的编制与具体的编程语言及编译器无关只要遵循约定的DLL接口规范和调用方式,用各种语言编写的DLL都可以相互调用。
譬如Windows提供的系统DLL(其中包括了Windows的API),在任何开发环境中都能被调用,不在乎其是Visual Basic、Visual C++还是Delphi。
(2)动态链接库随处可见我们在Windows目录下的system32文件夹中会看到kernel32.dll、user32.dll 和gdi32.dll,windows的大多数API都包含在这些DLL中。
kernel32.dll中的函数主要处理内存管理和进程调度;user32.dll中的函数主要控制用户界面;gdi32.dll中的函数则负责图形方面的操作。
一般的程序员都用过类似MessageBox的函数,其实它就包含在user32.dll 这个动态链接库中。
由此可见DLL对我们来说其实并不陌生。
(3)VC动态链接库的分类Visual C++支持三种DLL,它们分别是Non-MFC DLL(非MFC动态库)、MFC Regular DLL(MFC规则DLL)、MFC Extension DLL(MFC扩展DLL)。
非MFC动态库不采用MFC类库结构,其导出函数为标准的C接口,能被非MFC或MFC编写的应用程序所调用;MFC规则DLL包含一个继承自CWinApp的类,但其无消息循环;MFC扩展DLL采用MFC的动态链接版本创建,它只能被用MFC类库所编写的应用程序所调用。
由于本文篇幅较长,内容较多,势必需要先对阅读本文的有关事项进行说明,下面以问答形式给出。
问:本文主要讲解什么内容?答:本文详细介绍了DLL编程的方方面面,努力学完本文应可以对DLL有较全面的掌握,并能编写大多数DLL程序。
问:如何看本文?答:本文每一个主题的讲解都附带了源代码例程,可以随文下载(每个工程都经WINRAR压缩)。
所有这些例程都由笔者编写并在VC++6.0中调试通过。
当然看懂本文不是读者的最终目的,读者应亲自动手实践才能真正掌握DLL 的奥妙。
问:学习本文需要什么样的基础知识?答:如果你掌握了C,并大致掌握了C++,了解一点MFC的知识,就可以轻松地看懂本文。
2.静态链接库对静态链接库的讲解不是本文的重点,但是在具体讲解DLL之前,通过一个静态链接库的例子可以快速地帮助我们建立“库”的概念。
图1建立一个静态链接库如图1,在VC++6.0中new一个名称为libTest的static library工程(单击此处下载本工程),并新建lib.h和lib.cpp两个文件,lib.h和lib.cpp的源代码如下://文件:lib.h#ifndef LIB_H#define LIB_Hextern"C"int add(int x,int y);//声明为C编译、连接方式的外部函数#endif//文件:lib.cpp#include"lib.h"int add(int x,int y){return x+y;}编译这个工程就得到了一个.lib文件,这个文件就是一个函数库,它提供了add的功能。
将头文件和.lib文件提交给用户后,用户就可以直接使用其中的add 函数了。
标准Turbo C2.0中的C库函数(我们用来的scanf、printf、memcpy、strcpy 等)就来自这种静态库。
下面来看看怎么使用这个库,在libTest工程所在的工作区内new一个libCall静态链接库的调用就是这么简单,或许我们每天都在用,可是我们没有明白这个概念。
代码中#pragma comment(lib,"..\\debug\\libTest.lib")的意思是指本文件生成的.obj文件应与libTest.lib一起连接。
如果不用#pragma comment指定,则可以直接在VC++中设置,如图2,依次选择tools、options、directories、library files 菜单或选项,填入库文件路径。
图2中加红圈的部分为我们添加的libTest.lib文件的路径。
图2在VC中设置库文件路径这个静态链接库的例子至少让我们明白了库函数是怎么回事,它们是哪来的。
我们现在有下列模糊认识了:(1)库不是个怪物,编写库的程序和编写一般的程序区别不大,只是库不能单独执行;(2)库提供一些可以给别的程序调用的东东,别的程序要调用它必须以某种方式指明它要调用之。
以上从静态链接库分析而得到的对库的懵懂概念可以直接引申到动态链接库中,动态链接库与静态链接库在编写和调用上的不同体现在库的外部接口定义及调用方式略有差异。
3.库的调试与查看在具体进入各类DLL的详细阐述之前,有必要对库文件的调试与查看方法进行一下介绍,因为从下一节开始我们将面对大量的例子工程。
由于库文件不能单独执行,因而在按下F5(开始debug模式执行)或CTRL+F5(运行)执行时,其弹出如图3所示的对话框,要求用户输入可执行文件的路径来启动库函数的执行。
这个时候我们输入要调用该库的EXE文件的路径就可以对库进行调试了,其调试技巧与一般应用工程的调试一样。
图3库的调试与“运行”通常有比上述做法更好的调试途径,那就是将库工程和应用工程(调用库的工程)放置在同一VC工作区,只对应用工程进行调试,在应用工程调用库中函数的语句处设置断点,执行后按下F11,这样就单步进入了库中的函数。
第2节中的libTest和libCall工程就放在了同一工作区,其工程结构如图4所示。
图4把库工程和调用库的工程放入同一工作区进行调试上述调试方法对静态链接库和动态链接库而言是一致的。
所以本文提供下载的所有源代码中都包含了库工程和调用库的工程,这二者都被包含在一个工作区内,这是笔者提供这种打包下载的用意所在。
动态链接库中的导出接口可以使用Visual C++的Depends工具进行查看,让我们用Depends打开系统目录中的user32.dll,看到了吧?红圈内的就是几个版本的MessageBox了!原来它真的在这里啊,原来它就在这里啊!图5用Depends查看DLL当然Depends工具也可以显示DLL的层次结构,若用它打开一个可执行文件则可以看出这个可执行文件调用了哪些DLL。
好,让我们正式进入动态链接库的世界,先来看看最一般的DLL,即非MFC DLL。
4动态库4.1一个简单的dll第2节给出了以静态链接库方式提供add函数接口的方法,接下来我们来看看怎样用动态链接库实现一个同样功能的add函数。
如图6,在VC++中new一个Win32Dynamic-Link Library工程dllTest(单击此处下载本工程)。
注意不要选择MFC AppWizard(dll),因为用MFC AppWizard(dll)建立的将是第5、6节要讲述的MFC动态链接库。
图6建立一个非MFC DLL在建立的工程中添加lib.h及lib.cpp文件,源代码如下:/*文件名:lib.h*/#ifndef LIB_H#define LIB_Hextern"C"int__declspec(dllexport)add(int x,int y);#endif/*文件名:lib.cpp*/#include"lib.h"int add(int x,int y){return x+y;}与第2节对静态链接库的调用相似,我们也建立一个与DLL工程处于同一工作区的应用工程dllCall,它调用DLL中的函数add,其源代码如下:#include<stdio.h>#include<windows.h>typedef int(*lpAddFun)(int,int);//宏定义函数指针类型int main(int argc,char*argv[]){HINSTANCE hDll;//DLL句柄lpAddFun addFun;//函数指针hDll=LoadLibrary("..\\Debug\\dllTest.dll");if(hDll!=NULL){addFun=(lpAddFun)GetProcAddress(hDll,"add");if(addFun!=NULL){int result=addFun(2,3);printf("%d",result);}FreeLibrary(hDll);}return0;}分析上述代码,dllTest工程中的lib.cpp文件与第2节静态链接库版本完全相同,不同在于lib.h对函数add的声明前面添加了__declspec(dllexport)语句。
这个语句的含义是声明函数add为DLL的导出函数。
DLL内的函数分为两种:(1)DLL导出函数,可供应用程序调用;(2)DLL内部函数,只能在DLL程序使用,应用程序无法调用它们。
而应用程序对本DLL的调用和对第2节静态链接库的调用却有较大差异,下面我们来逐一分析。
首先,语句typedef int(*lpAddFun)(int,int)定义了一个与add函数接受参数类型和返回值均相同的函数指针类型。
随后,在main函数中定义了lpAddFun的实例addFun;其次,在函数main中定义了一个DLL HINSTANCE句柄实例hDll,通过Win32 Api函数LoadLibrary动态加载了DLL模块并将DLL模块句柄赋给了hDll;再次,在函数main中通过Win32Api函数GetProcAddress得到了所加载DLL模块中函数add的地址并赋给了addFun。
经由函数指针addFun进行了对DLL 中add函数的调用;最后,应用工程使用完DLL后,在函数main中通过Win32Api函数FreeLibrary释放了已经加载的DLL模块。