动态链接库编程

概念:


Dynamic-Link Library,DLL (*.dll),实际上是一个“存放了可执行代码”的仓库。
通常我们写的代码经过编译器编译后,形成二进制代码,使计算机可以直接执行,如*.exe程序。
Exe程序中,将工程中所有代码编译在一起,并将main函数编译在最开头,形成“执行入口”,计算机可以通过该入口,执行程序。
DLL存放的也是可执行二进制代码,但DLL中没有“入口函数”,只是存放了一组特定功能的代码。如果有一个exe程序需要用到dll中的代码,可以不经过编译,直接找到dll中对应的二进制代码执行即可,从而实现了代码共享。
DLL是Windows操作系统中,实现代码共享的最重要机制。实际上,Win系统的核心,就是一组DLL。

DLL的意义:

降低程序大小和编写成本,避免重复开发;
实现代码安全:不需要开放源代码,只开放dll,用户能够使用程序功能,但却无法破译二进制代码,从而无法抄袭代码。(——这也是微软的生存之道)
DLL可以方便实现系统升级。目前大多数所谓“软件升级”,都是通过替换dll,实现功能改进和性能提高。

程序中的动态链接过程
程序在使用dll的地方
需要指定使用哪个dll
然后通过索引找到dll
中代码的地址,
然后程序跳到dll代码中,
执行代码,
执行完后返回原代码
继续执行。




DLL的选择性“导出”
比如,我们实现了一个复杂的算法,涉及许多类、变量、函数。我们希望别人可以使用我们的算法,但不知道算法实现的结构。

利用Dll,我们可以选择性的将该算法的输入、输出写成一个函数,并作为“导出函数”提供给外界使用,其余函数放在工程里,不导出,这样,整个dll就只有这一个函数能使用,其余的所有函数外界是无法调用的。

DLL可能内部实现非常复杂,但对外只导出部分函数,使得代码维护变得容易,只要导出函数不变,内部的实现可以任意更改。

结构:DLL组成和文件结构

一个完整的动态链接库包括3个文件:
头文件(*.h)——定义dll中可用的变量、类、函数、资源,        它们对应的代码在cpp文件中,并被编译为dll。
库文件(*.lib)——定义了dll代码文件中的链接地址信息等
代码文件(*.dll)——包含可执行代码和一张表,存放代码中可用内容的信息。

种类:VC开发DLL种类

Win32 DLL :最普通的dll,常用来写C代码
常规MFC DLL:采用MFC中现有的类编写的功能函数。可以使用MFC功能,但没有自己的消息循环。
MFC扩展DLL:如果要将派生于MFC的类写成dll,就必须采用MFC扩展DLL来写。

Win32 DLL的创建和使用

Win32 DLL(1)

Win32 DLL是最简单的dll,类似于Win32 Console程序,它能够将C/C++函数、变量封装起来。
创建DLL工程
导出函数、变量、类
在新的工程中使用DLL
DLL的调试

Win32 DLL(2)——创建DLL

创建DLL:新建向导->选择“Win32 Dinamic-link Library”
建立名为“W15_1_DLL”的工程
下一步,选择“一个空的DLL工程”,点击“完成”

Win32 DLL(3)——导出函数

空工程中,没有任何代码,我们新建一对代码文件,如W15_1_dll.cpp和W15_1_dll.h
在h文件中,写一句代码:
extern  “C”  int  _declspec(dllexport)  FactorialFunc(int a);
extern表明这是一个全局函数。所有导出函数都是全局的。
“C”指明导出一个C风格函数。不需要改
_declspec(dllexport) 是导出关键词,在函数声明时带有该关键词的函数,都会被导出
后面是函数名。

Win32 DLL(4)——导出函数

编写CPP代码:
#include "W15_1_DLL.h”
int FactorialFunc(int a)
{
 int fact = 1;
 int i;
 for (i=1;i<=a;i++){
  fact *= i;
 }
 return fact;
}

Win32 DLL(5)——使用DLL


DLL不能单独运行,必须通过其他程序“链接”使用。在程序中使用dll有两种方法:隐式链接、显式链接

隐式链接时,系统在启动程序的时候同时也启动dll,在代码中可以像使用一般函数一样,使用dll中的函数。——需要使用dll的*.h文件和*.lib文件

显式链接时,必须在代码中明确指出dll名称,并且使用指针调用dll中的导出函数,在使用完后,需要释放dll。——不需要使用lib文件,仅需要*.h文件

Win32 DLL(6)—配置dll调试环境


DLL无法单独运行,因此调试dll往往需要建立一个与之搭配的“dll测试工程”。——很重要
Step1:准备workspace。
建一个新的目录W15_1,将dll工程copy到这个目录里,将原来的W15_1_dll.dsw拿出来放在W15_1中。
双击之,这时由于找不到原有的dsp工程文件,会弹出对话框,提示选择工程源文件,这时,找到: W15_1_dll.dsp,确定。
在当前工作空间中建立一个console类型的工程,名为“W15_1_TEST”选择“添加到当前工作空间”。
以后,两个工程分别叫做“DLL工程” 和“TEST工程”。

Step2:配置工程属性——非常重要
菜单->工程->设置,选择TEST工程,选择“调试”页,修改其中两项:
菜单->工程->设置,选择TEST工程,选择“连接”页,修改输出文件名:
同样,修改DLL工程的以上三个属性:
可调式对话,选择test.exe
工作目录,选择“  ..\debug  ”
输出文件,选择 “..\Debug\W15_1_DLL.dll”
最后,修改工程依赖性:菜单->工程->从属性
令test工程依赖于dll工程

Win32 DLL(10)——隐式链接


Step1:在test工程的W15_1_TEST.cpp开头处添加对dll头文件的引用;
Step2:向程序中添加DLL库文件,如下:
#include "../W15_1_DLL/W15_1_DLL.h"
#pragma comment(lib,"../W15_1_DLL/debug/W15_1_DLL.lib")
这样,就可以在下面的程序中使用阶乘函数了。
while(true){
printf("输入一个数字:\n");
scanf("%d",&n);
rt=FactorialFunc(n);
printf("阶乘结果为:%d\n",rt);
}

Win32 DLL(11)——显式链接

隐式链接,程序在启动时就加载整个dll。在程序规模比较大,使用大量dll时,这样的方式效率很低。

显式链接不需要lib文件,其加载和卸载都需要用户通过代码完成。分别由API函数:LoadLibrary和FreeLibrary,控制加载dll和卸载dll。

显示链接在使用时非常灵活,甚至不需要dll头文件,但用户必须要知道dll中的导出函数的信息,并且告诉程序该信息。

Win32 DLL(12)——显式链接

把test工程中所有隐式导入dll代码和调用注释掉。

Step1:定义要使用的dll函数指针,使用typedef
typedef int (*W15_1_DLL_func1)(int);
int FactorialFunc(int a);

定义的dll函数指针类型,必须要与原dll函数具有相同的参数和返回值;
定义该类型后,即可用W15_1_DLL_func1来“当作”dll中的FactorialFunc使用。

Step2:装载dll
HINSTANCE hdll;
hdll = LoadLibrary("W15_1_DLL.dll");
HINSTANCE是Windows的“实例句柄”,如果成功打开dll,则hdll会赋值一个指针。反之hdll==NULL

Step3:使用、卸载dll函数
if(hdll!=NULL){
W15_1_DLL_func1 mydllfunc=(W15_1_DLL_func1)GetProcAddress(hdll,"FactorialFunc");
rt = mydllfunc(7);
printf("显示链接,运算结果为%d\n",rt);
FreeLibrary(hdll);
}
GetProcAddress函数中,指明了要调用的dll函数名,该函数的参数和返回值则由先前定义的函数指针来确定。

Win32 DLL(15)——dll的调试


如果需要调试dll的代码,必须满足如下条件:

Dll工程是目前的活动工程

Dll工程中的“可执行调式对话”,必须正确设置并且可调试

Win32 DLL(16)——MFC框架

以上展示了一个最简单的Win32DLL的使用方法。大多数情况下,尤其是以后做算法的时候,写这样的dll应该就够用。

MFC提供了一套win32 dll模板,在新建dll时,如果选择“一个简单dll”,或者“能够导出某些符号的dll”,就会自动生成一个框架。我们以后者为例一起看一下。

Win32 DLL(17)——MFC框架

dll.h文件

dll.cpp文件

MFC DLL的创建和使用

MFC常规DLL(1)——特点、创建

特点:
可以在dll内部任意使用MFC类库编写程序。
生成的dll可以被所有支持dll技术的程序调用。
只能导出“非MFC风格”的函数,也无法导出MFC派生类

创建:新建,选择“MFC APPWizard [dll]”,选择:动态链接库使用共享MFC DLL

MFC常规DLL(2)——编程

建立工程后,在工程中添加一个对话框资源。添加几个控件,添加类、变量。

MFC常规DLL(3)——编程

在创建之后,工程并没有导出任何函数,需要自己编程实现。
向程序中添加cpp文件,命名为DlgExport.cpp,添加:
添加对应的h文件,声明dll导出函数
编译通过。
仿照上一个例子,建立dll的测试工程。
在test工程中,用隐式链接方式,调用dll。
#include "../W15_2_DLL/DlgExport.h"
#pragma comment(lib,"../W15_2_DLL_debug/W15_2_DLL.lib")

int main(int argc,char* argv[]){
int score1,score2;
printf("输入语文成绩:\n");
scanf("%d",&score1);
printf("输入数学成绩:\n");
scanf("%d",&score2);
return 0;
}

可以直接在控制台程序中直接调用MFC对话框

MFC扩展DLL(1)

可以使用dll来实现从MFC中派生一些可重用类

使用扩展DLL,可将MFC程序模块化,将若干个组建拼起来,作为一个整体使用。这样的方式,就好象我们用MFC现有的“零件”拼装成一个成型的“组建”,可以被其他程序方便的使用。

通过这样的方式,可以定制MFC,制作自己的控件、属性对话框等。

动态链接库小结

DLL是windows的核心技术之一。正是通过dll技术,Windows能将庞大的操作系统逐级分解,逐层设计,逐步完善,发展至今。

直到今天,微软仍在以dll为核心,开发windows。我们使用的补丁、编程使用的接口、各种软件对系统的调用,都是通过一个个dll导出函数来实现功能。

掌握dll的基本编写方法,也是我们保护自己工作成果的最简单的方式。

Related Articles

Quote Of The Day