第十八课:动态链接库
一、概念:
1、 静态链接库:
应用程序从函数库中得到所的函数的执行代码,然后把招生代码自身的执行文件中,应用程序在运行时,不再需要函数库的支持。
2、 动态链接库:
应用程序的中不包含函数库中的函数的执行代码,编译和连接时只是包含包含它们的参考,运行时再将它们的执行代码加入内存,所以在程序运行时需要函数的支持。
二、编写DLL:
建立一个Windows Dynamic-Link Library,选择建一个An Empty Dll Project,加入一个CPP文件,
int add(int x,int y)
{
return x+y;
}
extern "C" _declspec (dllexport) int add3(int x,int y,int z)
{
return add(x,y)+z;
}
说明:
1. Add是一个供DLL中其它函数调用的函数, Add3是DLL提供给其它应用程序调用的函数。
2. 当DLL中含有输出函数时,编译DLL时会生成DLL文件和LIB文件。LIB文件称为DLL的导入库,它是一个特殊的库文件。它不包含执行代码,只是用来提供给链接器关于DLL函数在DLL中的入口信息,从而使得可执行程序 中也不会包含所调用DLL函数 的代码 ,只保留对DLL函数的动态链接参考。
3. 导出函数的两种方法:
a. 使用微软专用的_declspec (dllexport):(如上面的例子)
cpp文件在编译为OBJ文件时要对函数进行重新命名,C语言会把函数name重新命名为_name,而C++会重新命名为_name@@decoration,
extern "C"表示用C语言的格式将函数重命名。
b. 使用模块定义文件:
模块定义文件即DEF文件,是包含一个或多个模块定义语句的文本文件,用来描述动态链接库的各种属性。
一个最小的·DEF文件包括以下模块定义语句:
l 第一条语句必须是LIBRARY语句,用来说明动态链接库的名字。
l 有EXPORTS语句之后列出动态链接库要输出的函数。用户可以在每个要输出的函数指定一个输出序列号,这只要在对应的函数名之后 加上@符号心腹一个数字即可。注意:这个数字应该是从1到输出函数总数之间的没有重复的数字。指定函数的序列号后,动态加载该函数时,就会通过序列号而不是函数名来检索该函数,因此处理速度将会加快,战胜的内存会减少,从而增加动态链接时的效率。
将上面的例子改为用DEF文件输出:
int add(int x,int y)
{
return x+y;
}
extern "C" int add3(int x,int y,int z)
{
return add(x,y)+z;
}
在工程中加入一文本文件,其名字为dll.def,加入以下语句:
LIBRARY dll
EXPORTS
add3 @ 1
三、访问动态链接库:
新建一个基于对话框的MFC工程,
1. 静态调用:
通过编译器提供给应用程序关于DLL的名称,以及DLL函数的链接参考。这种方式不需要在程序中用代码将DLL加载到内存。
a. 将DLL和LIB文件拷贝到工程目录下,最好在工程的DEBUG目录下也拷贝一份。
b. 在ProjectàSettingsàLink中的Object/Library Modules:填入LIB文件的名字。本例中是dll.lib。
c. 在对话框中加入一个按钮控件,添加它的响应函数OnButton1(),在其中加入调用代码:
CString str;
str.Format("%d",add3(3,4,5));
MessageBox(str);
d. 加入函数声明:
l 用_declspec (dllexport)导出函数的DLL
extern "C"_declspec (dllimport) int add3(int x,int y,int z);
l 用DEF文件导出函数的DLL
int add3(int x,int y,int z);
2. 动态调用:
在程序中用语句显式地加载DLL,编译器不需要知道任何关于DLL的信息。
a. 将DLL文件拷贝到工程目录下,最好在工程的DEBUG目录下也拷贝一份。
b. 在对话框中加入一个按钮控件,添加它的响应函数OnButton1(),在其中加入调用代码:
CString str;
typedef int (*PADD3)(int x,int y,int z);
//定义一种新的数据类型—指向函数的指针
PADD3 add3;
HINSTANCE hDll=LoadLibrary("dll");
//将动态链接库加载到内存
add3=(PADD3)GetProcAddress(hDll,"add3");
//得到DLL中指定函数的指针
str.Format("%d",add3(3,4,5));
MessageBox(str);
FreeLibrary(hDll);//释放应用程序对DLL的控制权
说明:如果DLL用DEF文件导出函数时为其指定了序列号,比如1,则
add3=(PADD3)GetProcAddress(hDll,"add3");
可以改为:
add3=(PADD3)GetProcAddress(hDll, MAKEINTRESOURCE(1));
四、其它:
1. Windows如何定位DLL
不论是静态调用还是动态调用DLL,Windows会按以下顺序寻找DLL:
a. 当前路径
b. Windows的system路径。用API函数GetSystemDirectory可以得到;
c. Windows路径。用API函数GetWindowsDirectory可以得到;
d. 环境变量PATH所指定 的路径。
2. DLL也可以有一个入口函数DllMain(),当然也可以没有。如果有,DllMain会在进程加载、进程缷载、线程加载和线程缷载DLL时自动调用。
3. 为了用户使用方便,应该将导出函数的声明放在一个头文件中。可以使用预处理命令简化更换__declspec(dllexport)和__declspec(dllimport)的操作。这样,DLL和应用程序 可以使用相同的头文件:
#ifdef DLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif
评论