FC导出对象的指针。
接下来,我们一起来具体讨论一下以上三种动态链接库的创建方法与调用方法。
二、非MFC动态链接库
2.1. Non-MFC DLL动态链接库的创建
在Visual C++6.0开发环境下,打开File/New/Project选项,可以选择Win32 Dynamic-Link Library或MFC AppWizard[dll] 来以不同的方式来创建Non-MFC Dll、Regular Dll、Extension Dll等不同种类的动态链接库。下面演示以Win32 Dynamic-Link Library方式创建一个简单Non-MFC Dll工程"MyDll"。
2.1.1. 创建一个Non-MFC Dll工程"MyDll"
(1) 在Visual C++6.0开发环境下,打开File/New/Project选项,选择Win32 Dynamic-Link Library创建一个简单DLL工程,并命名为"MyDll"。
图2-1-1 创建Non-MFC Dll工程"MyDll"
(2) 在建立的工程中添加lib.h及lib.cpp文件,源代码如下:
/* 文件名:lib.h */
#ifndef LIB_H
#define LIB_H
extern "C" int __declspec(dllexport)multi(int x,int y);
#endif
/* 文件名:lib.cpp */
#include "lib.h"
int multi(int x,int y) {
return x*y;
}
显然,以上建立的是一个返回两个数的乘积的动态链接库。
(3)建立一个与MyDll工程处于同一工作区的应用工程DllCall。
图2-1-2 创建应用工程"DllCall"
在工程"DllCall"创建源代码文件"DllCall.cpp",它调用DLL中的函数multi,其源代码如下:
#include
#include
typedef int(*lpmultiFunc)(int,int); //宏定义函数指针类型
int main()
{
HINSTANCE hDll; //DLL句柄
lpmultiFunc multiFunc; //函数指针
hDll = LoadLibrary("..\\Debug\\MyDll.dll");
if (hDll != NULL) {
multiFunc = (lpmultiFunc)GetProcAddress(hDll, "multi");
if (multiFunc != NULL) {
int result = multiFunc(3,5); //计算3与5的乘积
printf("%d\n",result);
}
FreeLibrary(hDll);
}
return 0;
}
(4) 编译,运行结果如下:
图2-1-3 运行结果
如上图所示,成功返回3*5的结果,表明此NON-MFC DLL创建成功。
2.1.2. 代码分析
分析上述代码,Mydll工程中的lib.h对函数multi的声明前面添加了__declspec(dllexport)语句。这个语句的含义是声明函数multi为DLL的导出函数。DLL内的函数分为两种:
(1) DLL导出函数,可供应用程序调用;
(2) DLL内部函数,只能在DLL程序使用,应用程序无法调用它们。
现在分析应用工程dllCall 对 DLL 的调用是如何实现的:
首先,语句typedef int ( * lpmultiFun)(int,int)定义了一个与multi函数接受参数类型和返回值均相同的函数指针类型。随后,在main函数中定义了lpmultiFunc的实例multiFunc;
其次,在函数main中定义了一个DLL HINSTANCE句柄实例hDll,通过Win32 Api函数LoadLibrary动态加载了DLL模块并将DLL模块句柄赋给了hDll;
再次,在函数main中通过Win32 Api函数GetProcAddress得到了所加载DLL模块中函数multi的地址并赋给了multiFunc。经由函数指针multiFunc进行了对DLL中add函数的调用;
最后,应用工程使用完DLL后,在函数main中通过Win32 Api函数FreeLibrary释放了已经加载的DLL模块。从而完成了对dll的调用。
通过以上例子分析可得出以下结论:
(1)DLL中需以某种特定的方式声明导出函数(或变量、类);
(2)应用工程需以某种特定的方式调用DLL的导出函数(或变量、类)。
2.2. 声明导出函数
DLL中导出函数的声明有两种方式:
一是像上述例子一样,在定义函数时使用导出关键字_declspec(dllexport);
另外一种方法是在创建DLL文件时使用模块定义文件.def。如以下例子:
下面的代码演示了怎样在 .def 文件中将函数multi声明为DLL导出函数(需在MyDll工程中添加lib.def文件):
LIBRARY Mydll
EXPORTS
multi@ 1
def文件的规则为:
(1)LIBRARY语句说明.def文件相应的DLL;
(2)EXPORTS语句后列出要导出函数的名称。可以在.def文件中的导出函数名后加@n,表示要导出函数的序号为n(在进行函数调用时,这个序号将发挥其作用);
(3).def 文件中的注释由每个注释行开始处的分号 (;) 指定,且注释不能与语句共享一行。
由此可以看出,例子中lib.def文件的含义为生成名为"MyDll"的动态链接库,导出其中的multi函数,并指定multi函数的序号为1。
2.3. DLL的调用方式
DLL有两种调用方式:
(1) 动态调用
在上述的例子中我们看到了由"LoadLibrary-GetProcAddress-FreeLibrary"系统Api提供的三位一体"DLL加载-DLL函数地址获取-DLL释放"方式,这种调用方式称为DLL的动态调用。
动态调用方式的特点是完全由编程者用 API 函数加载和卸载 DLL,程序员可以决定 DLL 文件何时加载或不加载,显式链接在运行时决定加载哪个 DLL 文件。
(2) 静态调用
静态调用方式的特点是由编译系统完成对DLL的加载和应用程序结束时 DLL 的卸载。当调用某DLL的应用程序结束时,若系统中还有其它程序使用该 DLL,则Windows对DLL的应用记录减1,直到所有使用该DLL的程序都结束时才释放它。静态调用方式简单实用,但不如动态调用方式灵活。
如以下例子所示:
将编译MyDll工程所生成的.lib和.dll文件拷入dllCall工程所在的路径,
图2-3-1 添加MyDll.lib和MyDll.dll文件
在dllCall源文件下执行下列代码:
#include
#pragma comment(lib,"MyDll.lib")
//.lib文件中仅仅是关于其对应DLL文件中函数的重定位信息
extern "C" __declspec(dllimport) multi(int x,int y);
int main(int argc, char* argv[ ])
{
int result = multi(3,5);
printf("静态调用结果:%d\n",result);
return 0;
}
编译运行,运行结果如下:
图2-3-2 运行结果
由上述代码可以看出,静态调用方式的顺利进行需要完成两个动作:
(1) 告诉编译器与DLL相对应的.lib文件所在的路径及文件名,#pragma comment(lib,"MyDll.lib")就是起这个作用。程序员在建立一个DLL文件时,连接器会自动为其生成
上一篇:毕业论文(宾馆管理系统)(2003doc)
下一篇:采用VC的伺服电机控制毕业论文(2003doc)