动态链接库(DLL)很重要,这不用说了,自从微软推出16位操作系统,以后每个版本的操作系统都非常依赖于DLL中的函数。实际上,windows操作系统中几乎所有的内容都由DLL以一种形式或另外一种形式代表着。比如:显示字体或图标存储在GDI DLL中,显示windows桌面和处理用户输入存储在User DLL中,windows编程所需的大量API函数存储在Kernal DLL中等等。
DLL的优点
为什么要这么广泛的使用DLL,就是因为它有很多优点。
最主要的一个优点是使用较少的资源,因为多个应用程序甚至是不同语言编写的应用程序都可以共享同一个DLL,这样就减少了代码在硬盘上和物理内存上的重复存放。
另外,DLL有助于促进模块式程序的开发。因为它的封装性、独立性好。
还有就是简化安装和升级。因为在软件升级的时候,开发人员只需修改DLL文件就可以了,如果DLL导出函数的接口没有变。而且程序代码也不需要重新编译,这大大提高了软件开发和维护的效率。
什么是DLL
DLL是一个包含可由多个程序同时使用的代码和数据的库,MSDN上是这样说的。DLL实际上是建立在客户/服务器通信的概念上的,包含很多个函数、类或资源的库文件。函数和数据被存储在一个DLL上,并由一个或多个客户导出使用,这些客户可以是应用程序,也可以是DLL。DLL与静态库不同,静态库情况下,函数和数据被编译成一个二进制文件(通常是.LIB文件),编译器在编译程序代码时,将这些函数和数据恢复出来并和程序的其它模块一起编译成可执行文件.exe,这个过程就是“静态链接”。在这个过程中,因为程序所需的函数和数据都是从库中复制过来的,所以在程序发行的时候,静态库不需要与可执行文件一起发行。
动态库就不同了,它有两个文件,一个是引入库文件(.LIB),一个是DLL文件。引入库文件中包含着导出函数的名字和地址,而实际的函数和数据是在DLL文件中的。所以我们会看见.LIB文件通常要比.DLL文件小的多。应用程序就是通过.LIB文件链接到所需要的DLL文件的,这时,DLL中的函数和数据并不复制到可执行文件中,所以在可执行中存放的不是被调用函数的代码,而是链接到此函数的地址,所以,.LIB文件和.DLL文件必须随可执行文件一起发行,否则会出错。
加载DLL的两种方式
在应用程序加载DLL时,有两种方式来调用DLL导出函数,加载时动态链接和运行时动态链接。加载时动态链接就是说应用程序可以像调用本地函数一样对DLL导出的函数进行显示调用。但在这种方式中,我们必须提供.h和.LIB文件,以保证加载时正确解析导出函数的位置。运行时动态链接中,应用程序可以运行时用LoadLibrary和LoadLibraryEx函数来完成对DLL的加载。加载成功以后,用GetProcAddress函数获得DLL导出函数的地址,当我们使用完导出函授时,要调用FreeLibrary函数来释放加载的DLL。这种方式中,不需要提供.LIB文件。
那么什么时候用加载时动态链接,什么时候使用运行时动态链接呢?这要看你的应用程序的需要。如果你的应用程序很注重初始启动时的性能,则使用运行时动态链接比较好。如果是为了图方便,可以像调用本地函数一样调用DLL导出的函数,可以考虑使用加载时动态链接。如果想在应用程序中实现分支,并根据不同需要加载不同的模块,比如开发多语言版本的程序,就要使用运行时动态链接了。
使用DLL
首先要导出DLL函数,我们可以向导出DLL函数中添加关键字,也可以创建模块定义文件(.def)来列出DLL导出函数。对于要导出的每一个函数,我们都需要使用关键字对其声明:
__declspec(dllexport)
在应用程序中要想使用这些导出函数,也需要使用关键字来声明要使用的DLL导出函数:
__declspec(dllimport)
我们还可以使用模块定义文件来声明DLL导出函数,这样,我们就不需要向DLL导出函数中添加关键字了。如:
// myDLL.def
//
LIBRARY "myDLL"
EXPORTS
HelloWorld
//
LIBRARY "myDLL"
EXPORTS
HelloWorld
示例DLL和应用程序
这是一个在Visual C++中用“Win32动态链接库”项目类型创建的示例程序。
这是一个在Visual C++中用“Win32动态链接库”项目类型创建的示例程序。
// myDLL.cpp
//
#include "stdafx.h"
#define EXPORTING_DLL
#include "sampleDLL.h"
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
void HelloWorld()
{
MessageBox( NULL, TEXT("Hello World"), TEXT("In a DLL"), MB_OK);
}
//
#include "stdafx.h"
#define EXPORTING_DLL
#include "sampleDLL.h"
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
void HelloWorld()
{
MessageBox( NULL, TEXT("Hello World"), TEXT("In a DLL"), MB_OK);
}
// File: myDLL.h
//
#ifndef INDLL_H
#define INDLL_H
#ifdef EXPORTING_DLL
extern __declspec(dllexport) void HelloWorld() ;
#else
extern __declspec(dllimport) void HelloWorld() ;
#endif
#endif
//
#ifndef INDLL_H
#define INDLL_H
#ifdef EXPORTING_DLL
extern __declspec(dllexport) void HelloWorld() ;
#else
extern __declspec(dllimport) void HelloWorld() ;
#endif
#endif
下面的程序是一个win32应用程序,该程序调用myDLL中的导出函数HelloWorld。
// myApp.cpp
//
#include "stdafx.h"
#include "myDLL.h"
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
HelloWorld();
return 0;
}
//
#include "stdafx.h"
#include "myDLL.h"
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
HelloWorld();
return 0;
}
注意:在加载时动态链接中,必须链接在生成myDLL项目时创建的myDLL.lib引入库。
在运行时动态链接中,应该这样调用myDLL.dll中的导出函数:
typedef VOID (*DLLPROC) (LPTSTR);
HINSTANCE hinstDLL;
DLLPROC HelloWorld;
BOOL fFreeDLL;
hinstDLL = LoadLibrary("myDLL.dll");
if (hinstDLL != NULL)
{
HelloWorld = (DLLPROC) GetProcAddress(hinstDLL, "HelloWorld");
if (HelloWorld != NULL)
(HelloWorld);
fFreeDLL = FreeLibrary(hinstDLL);
}