2024年3月28日发(作者:)

    本文由JaneWang201102贡献

    doc1。

    【转】如何用 VC++创建及调用 DLL

    (2010-12-01 13:57:33)

    一、前言 自从微软推出 16 位的 Windows 操作系统起,此后每种版本的 Wind

ows 操作系统都非常依赖于动态链接库(DLL)中的函数和数据,实际上 Windows 操作

系统中几乎所有的内容都由 DLL 以一种或另外一种形式 代表着,例如显示的字体和

图标存储在 GDI DLL 中、显示 Windows 桌 面和处理用户的输入所需要的代码被存储

在一个 User DLL 中、 Windows 编程所需要的大量的 API 函数也被包含在 Kernel 

DLL 中。 在 Windows 操作系统中使用 DLL 有很多优点, 最主要的一点是多个应 用

程序、甚至是不同语言编写的应用程序可以共享一个 DLL 文件,真 正实现了资源"共

享",大大缩小了应用程序的执行代码,更加有效的利 用了内存;使用 DLL 的另一个

优点是 DLL 文件作为一个单独的程序模 块,封装性、独立性好,在软件需要升级的

时候,开发人员只需要修改 相应的 DLL 文件就可以了,而且,当 DLL 中的函数改变

后,只要不是 参数的改变,程序代码并不需要重新编译。 这在编程时十分有用, 大

大提 高了软件开发和维护的效率。 既然 DLL 那么重要,所以搞清楚什么是 DLL、如

何在 Windows 操作系 统中开发使用 DLL 是程序开发人员不得不解决的一个问题。本

文针对 这些问题,通过一个简单的例子,即在一个 DLL 中实现比较最大、最

    小整数这两个简单函数,全面地解析了在 Visual C++编译环境下编程实 现 DL

L 的过程,文章中所用到的程序代码在 Windows98 系统、Visual C++6.0 编译环境下

通过。

    二、DLL 的概念 DLL 是建立在客户/服务器通信的概念上,包含若干函数、类或

资源的 库文件,函数和数据被存储在一个 DLL(服务器)上并由一个或多个客 户导

出而使用,这些客户可以是应用程序或者是其它的 DLL。DLL 库 不同于静态库,在静

态库情况下,函数和数据被编译进一个二进制文件 (通常扩展名为*.LIB),Visual

 C++的编译器在处理程序代码时将从静 态库中恢复这些函数和数据并把他们和应用程

序中的其他模块组合在 一起生成可执行文件。这个过程称为"静态链接",此时因为应

用程序所 需的全部内容都是从库中复制了出来, 所以静态库本身并不需要与可执 行

文件一起发行。 在动态库的情况下,有两个文件,一个是引入库(.LIB)文件,一个

是 DLL 文件,引入库文件包含被 DLL 导出的函数的名称和位置,DLL 包 含实际的函

数和数据,应用程序使用 LIB 文件链接到所需要使用的 DLL 文件, 库中的函数和数

据并不复制到可执行文件中, 因此在应用程 序的可执行文件中,存放的不是被调用

的函数代码,而是 DLL 中所要 调用的函数的内存地址, 这样当一个或多个应用程序

运行是再把程序代 码和被调用的函数代码链接起来,从而节省了内存资源。从上面的

说明

    可以看出,DLL 和.LIB 文件必须随应用程序一起发行,否则应用程序 将会产生

错误。 微软的 Visual C++支持三种 DLL,它们分别是: Non-MFC Dll(非 MFC 动态

库) Regular Dll(常规 DLL) Extension Dll(扩展 DLL)。 Non-MFC DLL: 指的

是不用 MFC 的类库结构, 直接用 C 语言写的 DLL, 其导出的函数是标准的 C 接口

,能被非 MFC 或 MFC 编写的应用程序 所调用。 Regular DLL:和下述的 Extension

 Dlls 一样,是用 MFC 类库编写的,它 的一个明显的特点是在源文件里有一个继承

 CWinApp 的类(注意:此 类 DLL 虽然从 CWinApp 派生,但没有消息循环),被导出

的函数是 C 函数、C++类或者 C++成员函数(注意不要把术语 C++类与 MFC 的微 软

基础 C++类相混淆),调用常规 DLL 的应用程序不必是 MFC 应用程 序, 只要是能

调用类 C 函数的应用程序就可以, 它们可以是在 Visual C++、 Dephi、 Visual B

asic、 Borland C 等编译环境下利用 DLL 开发应用程序。 常规 DLL 又可细分成静

态链接到 MFC 和动态链接到 MFC 上的,这两 种常规 DLL 的区别将在下面介绍。与

常规 DLL 相比,使用扩展 DLL

    用于导出增强 MFC 基础类的函数或子类,用这种类型的动态链接库, 可以用来

输出一个从 MFC 所继承下来的类。 Extension Dll:扩展 DLL 是使用 MFC 的动态链

接版本所创建的,并且 它只被用 MFC 类库所编写的应用程序所调用。例如你已经创

建了一个 从 MFC 的 CtoolBar 类的派生类用于创建一个新的工具栏,为了导出这 个

page 1

类, 你必须把它放到一个 MFC 扩展的 DLL 中。 扩展 DLL 和常规 DLL 不一样,它

没有一个从 CWinApp 继承而来的类的对象,所以,开发人 员必须在 DLL 中的 DllM

ain 函数添加初始化代码和结束代码。

    三、动态链接库的创建 在 Visual C++6.0 开发环境下,打开 FileNewProject

 选项,可以选择 Win32 Dynamic-Link Library 或 MFC AppWizard[dll]来以不同的

方式来 创建 Non-MFC Dll、Regular Dll、Extension Dll 等不同种类的动态链接 库

。 1. Win32 Dynamic-Link Library 方式创建 Non-MFC DLL 动态链接库 每一个 D

LL 必须有一个入口点,这就象我们用 C 编写的应用程序一 样,必须有一个 WINMAI

N 函数一样。在 Non-MFC DLL 中 DllMain 是 一个缺省的入口函数,你不需要编写自

己的 DLL 入口函数,用这个缺 省的入口函数就能使动态链接库被调用时得到正确的

初始化。 如果应用 程序的 DLL 需要分配额外的内存或资源时,或者说需要对每个进

程或

    线程初始化和清除操作时,需要在相应的 DLL 工程的.CPP 文件中对 DllMain(

)函数按照下面的格式书写。

    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_PROCES

S_DETACH: …… } return TRUE; } 参数中:

    hMoudle:是动态库被调用时所传递来的一个指向自己的句柄(实际上, 它是指

向_DGROUP 段的一个选择符);

    ul_reason_for_call:是一个说明动态库被调原因的标志,当进程或线程 装入

或卸载动态链接库的时候,操作系统调用入口函数,并说明动态链 接库被调用的原因

,它所有的可能值为:

    DLL_PROCESS_ATTACH: 进程被调用 DLL_THREAD_ATTACH: 线程被调用 DLL_PROC

ESS_DETACH: 进程被停止 DLL_THREAD_DETACH: 线程被停止 lpReserved :为保留参

数。 到此为止,DLL 的入口函数已经写了,剩下部分的实现也不难,你可以 在 DLL

 工程中加入你所想要输出的函数或变量了。

    我们已经知道 DLL 是包含若干个函数的库文件,应用程序使用 DLL 中的函数之

前,应该先导出这些函数,以便供给应用程序使用。要导出 这些函数有两种方法,一

是在定义函数时使用导出关键字 _declspec(dllexport),另外一种方法是在创建 DL

L 文件时使用模块定义 文件.Def。需要读者注意的是在使用第一种方法的时候,不能

使用 DEF 文件。下面通过两个例子来说明如何使用这两种方法创建 DLL 文件。 1)

使用导出函数关键字_declspec(dllexport)创建 MyDll.dll,该动态链接 库中有两个

函数, 分别用来实现得到两个数的最大和最小数。 MyDll.h 在 和 MyDLL.cpp 文件

中分别输入如下原代码:

    //MyDLL.h

    extern "C" _declspec(dllexport) int Max(int a, int b); extern "C" _de

clspec(dllexport) int Min(int a, int b); //MyDll.cpp #i nclude #i nclude

"MyDll.h" int Max(int a, int b) { if(a>=b)return a; else return b; } int M

in(int a, int b) { if(a>=b)return b; else return a; } 该动态链接库编译成功

后,打开 MyDll 工程中的 debug 目录,可以看到 MyDll.dll、MyDll.lib 两个文件

。LIB 文件中包含 DLL 文件名和 DLL 文 件中的函数名等,该 LIB 文件只是对应该

 DLL 文件的"映像文件",与 DLL 文件中,LIB 文件的长度要小的多,在进行隐式链

接 DLL 时要用 到它。 读者可能已经注意到在 MyDll.h 中有关键字"extern C", 它

可以使 其他编程语言访问你编写的 DLL 中的函数。 2)用.def 文件创建工程 MyDl

    为了用.def 文件创建 DLL,请先删除上个例子创建的工程中的 MyDll.h 文件,

保留 MyDll.cpp 并在该文件头删除#include MyDll.h 语句,同时 往该工程中加入

一个文本文件, 命名为 MyDll.def。 一个.def 文件中只有 两个必需的部分: LIB

RARY 和 EXPORTS。 在该文件中加入如下代码: LIBRARY MyDll EXPORTS Max Min 其

中 LIBRARY 语句说明该 def 文件是属于相应 DLL 的 (在这里是指属 于 MyDLL.DL

L 的),EXPORTS 语句下列出要导出的函数名称。我们 可以在.def 文件中的导出函

page 2

数后加 @n,如 Max@1,Min@2,表示要 导出的函数顺序号, 在进行显式连时可以用

到它。 DLL 编译成功后, 该 打开工程中的 Debug 目录, 同样也会看到 MyDll.dl

l 和 MyDll.lib 文件。

    注:_declspec(dllexport)和.def 文件这两种方式不能同时使用。

    2.MFC AppWizard[dll]方式生成常规/扩展 DLL 在 MFC AppWizard[dll]下生成

 DLL 文件又有三种方式,在创建 DLL 是,要根据实际情况选择创建 DLL 的方式。一

种是常规 DLL 静态链接 到 MFC,另一种是常规 DLL 动态链接到 MFC。两者的区别是

:前者使

    用的是 MFC 的静态链接库,生成的 DLL 文件长度大,一般不使用这种 方式,

后者使用 MFC 的动态链接库,生成的 DLL 文件长度小;动态链 接到 MFC 的规则 D

LL 所有输出的函数应该以如下语句开始:

    AFX_MANAGE_STATE(AfxGetStaticModuleState( )) //此语句用来正确 地切换 

MFC 模块状态 最后一种是 MFC 扩展 DLL,这种 DLL 特点是用来建立 MFC 的派生 类

,Dll 只被用 MFC 类库所编写的应用程序所调用。前面我们已经介绍 过,Extensio

n DLLs 和 Regular DLLs 不一样,它没有一个从 CWinApp 继承而来的类的对象,编

译器默认了一个 DLL 入口函数 DLLMain()作 为对 DLL 的初始化,你可以在此函数中

实现初始化,代码如下:

    BOOL WINAPI APIENTRY DLLMain(HINSTANCE hinstDll,DWORD reason ,LPVOI

D flmpload) { switch(reason) { ……………//初始化代码; } return true; }

    参数 hinstDll 存放 DLL 的句柄,参数 reason 指明调用函数的原因, lpRes

erved 是一个被系统所保留的参数。对于隐式链接是一个非零值, 对于显式链接值是

零。 在 MFC 下建立 DLL 文件,会自动生成 def 文件框架,其它与建立传 统的 No

n-MFC DLL 没有什么区别,只要在相应的头文件写入关键字 _declspec(dllexport)函

数类型和函数名等,或在生成的 def 文件中 EXPORTS 下输入函数名就可以了。需要

注意的是在向其它开发人员分 发 MFC 扩展 DLL 时,不要忘记提供描述 DLL 中类的

头文件以及相应 的.LIB 文件和 DLL 本身, 此后开发人员就能充分利用你开发的扩

展 DLL 了。

    四、动态链接库 DLL 的链接 应用程序使用 DLL 可以采用两种方式:一种是隐

式链接,另一种是 显式链接。 在使用 DLL 之前首先要知道 DLL 中函数的结构信息

。 Visual C++6.0 在 VC in 目录下提供了一个名为 Dumpbin.exe 的小程序, 用它

可 以查看 DLL 文件中的函数结构。另外,Windows 系统将遵循下面的搜 索顺序来定

位 DLL: 1.包含 EXE 文件的目录,2.进程的当前工作目 录, 3.Windows 系统目

录, 4.Windows 目录,5.列在 Path 环境变 量中的一系列目录。

    1.隐式链接

    隐式链接就是在程序开始执行时就将 DLL 文件加载到应用程序当中。 实现隐式

链接很容易,只要将导入函数关键字_declspec(dllimport)函数 名等写到应用程序相

应的头文件中就可以了。 下面的例子通过隐式链接 调用 MyDll.dll 库中的 Min 函

数。首先生成一个项目为 TestDll,在 DllTest.h、 DllTest.cpp 文件中分别输入如

下代码:

    //Dlltest.h #pragma comment(lib,"MyDll.lib") extern "C"_declspec(dll

import) int Max(int a,int b); extern "C"_declspec(dllimport) int Min(int a

,int b); //TestDll.cpp #i nclude #i nclude"Dlltest.h" void main() {int a

; a=min(8,10) printf("比较的结果为%d ",a); }

    在创建 CallDllDemo.exe 文件之前,要先将 MFCDllDemo.dll 和 MFCDllDemo.

lib 拷贝到当前工程所在的目录下面,也可以拷贝到 windows 的 System 目录下。如

果 DLL 使用的是 def 文件,要删除

    TestDll.h 文件中关键字 extern "C"。TestDll.h 文件中的关键字 Progam co

mmit 是要 Visual C+的编译器在 link 时, 链接到 MyDll.lib 文件, 当然, 开发

人员也可以不使用#pragma comment(lib,"MyDll.lib")语句,而直接 在工程的 Set

ting->Link 页的 Object/Moduls 栏填入 MyDll.lib 既可。 2.显式链接 显式链接

是应用程序在执行过程中随时可以加载 DLL 文件,也可以随 时卸载 DLL 文件,这是

隐式链接所无法作到的,所以显式链接具有更 好的灵活性, 对于解释性语言更为合

page 3

适。 不过实现显式链接要麻烦一些。 在应用程序中用 LoadLibrary 或 MFC 提供的

 AfxLoadLibrary 显式的将 自己所做的动态链接库调进来, 动态链接库的文件名即

是上述两个函数 的参数,此后再用 GetProcAddress()获取想要引入的函数。自此,

你就 可以象使用如同在应用程序自定义的函数一样来调用此引入函数了。 在 应用程

序退出之前,应该用 FreeLibrary 或 MFC 提供的 AfxFreeLibrary 释放动态链接库

。 下面是通过显式链接调用 DLL 中的 Max 函数的例子。

    #i nclude #i nclude void main(void) { typedef int(*pMax)(int a,int 

b); typedef int(*pMin)(int a,int b); HINSTANCE hDLL;

    PMax Max HDLL=LoadLibrary("MyDll.dll");//加载动态链接库 MyDll.dll 文件

; Max=(pMax)GetProcAddress(hDLL,"Max"); A=Max(5,8); Printf("比较的结果为%

d ",a); FreeLibrary(hDLL);//卸载 MyDll.dll 文件; } 在上例中使用类型定义关

键字 typedef,定义指向和 DLL 中相同的函数 原型指针, 然后通过 LoadLibray()

将 DLL 加载到当前的应用程序中并返 回当前 DLL 文件的句柄,然后通过 GetProcA

ddress()函数获取导入到应 用程序中的函数指针,函数调用完毕后,使用 FreeLibr

ary()卸载 DLL 文件。在编译程序之前,首先要将 DLL 文件拷贝到工程所在的目录或

 Windows 系统目录下。 使用显式链接应用程序编译时不需要使用相应的 Lib 文件。

另外,使用 GetProcAddress()函数时, 可以利用 MAKEINTRESOURCE()函数直接使 用

 DLL 中函数出现的顺序号,如将 GetProcAddress(hDLL,"Min")改为 GetProcAddres

s(hDLL, MAKEINTRESOURCE(2)) 函数 Min()在 DLL 中 ( 的顺序号是 2),这样调用

 DLL 中的函数速度很快,但是要记住函数的 使用序号,否则会发生错误。 本文通过

通俗易懂的方式,全面介绍了动态链接库的概念、动态链接库 的创建和动态链接库的

链接,并给出个简单明了的例子,相信读者看了 本文后, 能够创建自己的动态链接

库并应用到后续的软件开发当中去了,

    当然,读者要熟练操作 DLL,还需要在大量的实践中不断摸索,希望本 文能起

到抛砖引玉的作用。

page 4