dll学习
Dll:动态链接库
动态链接库(dll)是包含共享函数库的二进制文件,可以被多个应用程序同时使用。建立应用程序的可执行文件时,不必将DLL连接到应用程序中,而是在运行时动态装载DLL,装载时DLL被映射到调用进程的地址空间中。通常我们在调用DLL时所需的DLL文件必须位于以下三个目录之一:
——(1)Windows的系统目录:/windows/system;
——(2)DOS中path所指出的任何目录;
——(3)程序所在的目录;
windows中有很多的dll文件,包括kernel.dll是内存管理的一些函数。含有图像处理的内存函数等等。
在VC中有两种导出函数的方法:
关键字_declspec 该方法比较简单。
模块定义文件。
dll主要有两部分组成,一是模块定义文件(.DEF),另一个是实现导出函数功能的源文件(C或C++文件)。函数分为内部函数和导出函数。
模块定义文件:
主要由语句说明:
1.LIBRARY 语句指出dll文件的名称。第一个语句必须是这个语句。
2.EXPORT语句指出被导出函数的名称。 EXPORT Add(),必备语句
3.可以使用DESCRIPTION语句对dll的用途进行说明,这个是可选项。
4.“;”对一行进行注释,可选项。
实现源文件:
实现入口函数的.cpp或.c文件,包括dll入口点的API函数和导出函数的代码。
在VC中好像很简单,并不需要写def,
我使用的是Visual C++ 2008 express Edition,当我新建一个工程时,选择编写dll文件,直接生成很多文件,包括dll入口文件dllmain.cpp和stdafx.cpp。和源文件sample.cpp以及头文件targetver.h stdafx.h,sample.h。
在dllmain.cpp中:
一个DLL必须有一个入口点,这就象我们用C编写的应用程序一样,必须有一个WINMAIN函数一样。
DllMain是一个缺省的入口函数,你不需要编写自己的DLL入口函数,并用linker的命令行的参数开关/ENTRY声明。用这个缺省的入口函数就能使动态连接库被调用时得到正确的初始化,当然了,你不要在初始化的时候填写使系统崩溃的代码了。参数中,hMoudle是动态库被调用时所传递来的一个指向自己的句柄(实际上,它是指向_DGROUP段的一个选择符)ul_reason_for_call是一个说明动态库被调原因的标志。当进程或线程
装入或卸载动态连接库的时候,操作系统调用入口函数,并说明动态连接库被调用的原因。它所有的可能值为:
DLL_PROCESS_ATTACH: 进程被调用
DLL_THREAD_ATTACH: 线程被调用
DLL_PROCESS_DETACH: 进程被停止
DLL_THREAD_DETACH: 线程被停止
lpReserved是一个被系统所保留的参数。
由于相同的原因,多个应用程序还可以同时共享DLL在内存中的同一份拷贝,这样就有效的节省了应用程序所占用的内存资源,减少了频繁的内存交换,从而提高了应用程序的执行效率。
由于DLL是独立于可执行文件的,因此,如果需要向DLL中增加新的函数或是增强现有函数的功能,只要原有函数的参数和返回值等属性不变,那么,所有使用该DLL的原有应用程序都可以在升级后的DLL的支持下运行,而不需要重新编译。这就极大的方便了应用程序的升级和售后支持。
DLL除了包括函数的执行代码以外,还可以只包括如图标、位图、字符串和对话框之类的资源,因此可以把应用程序所使用的资源独立出来做成DLL。对于一些常用的资源,把它们做到DLL中后,就可为多个应用程序所共享。
便于建立多语言的应用程序。我们可以把多语言应用程序中所使用的与语言相关的函数做到DLL中,只要不同语言的应用程序所调用的函数都具有相同的接口,这样就可以通过简单地更换DLL来实现多语言支持。
sample.h
这是VC提供的一个关键字,用它可在动态连接库中输出一个数据、一个函数或一个类。用这个关键字可省你不少事,你不用在.DEF文件
中说明我要输出这个类、那个函数的.
#ifdef FIRST_EXPORTS
#define FIRST_API __declspec(dllexport)
#else
#define FIRST_API __declspec(dllimport)
#endif
// This class is exported from the First.dll
class FIRST_API CFirst {
public:
CFirst(void);
// TODO: add your methods here.
};
extern FIRST_API int nFirst;
extern “C" FIRST_API int fnFirst(void);
编写DLL文件和调用DLL文件总结:
1、编写时要编写源文件和头文件。源文件定义函数原型、类。
如果是C++,可以定义类
如果是C,注意要以C的方式编译,要加上 extern “C”(这个很关键,否则利用取地址函数不分配函数地址,刚开始就这犯错误了)
2.在调用程序中,要设置函数指针。以及调用指针
typedef int(*phai)(int a,int b);
phai hai;//函数指针
HINSTANCE hDLL=NULL;
hDLL=LoadLibrary("sample.dll");//加载动态链接库MyDll.dll文件;
if (hDLL==NULL)
{
printf("Can not open");
return 0;
}
printf("test");
hai=(phai)GetProcAddress(hDLL,"Sum");
HINSTANCE 是“句柄型”数据类型。相当于装入到了内存的资源的ID。HINSTANCE对应的资源是instance.句柄实际上是一个 无符号长整数。但它是“句柄型”,所以你不能把它当成真的无符号长整数,拿来派别的用处,例如,不能拿来做四则运算。HINSTANCE常出现在 API 程序。
HMODULE 是代表应用程序载入的模块,win32系统下通常是被载入模块的线性地址。
HINSTANCE 在win32下与HMODULE是相同的东西,在Win32下还存在主要是因为win16
对于DLL的调用有两种:显示调用和隐式调用。
我前面所说的是显示调用,这种方法不需要.lib文件。头文件中要包含windows.h,#include "windows.h"
这种调用方式是指在应用程序中用LoadLibrary或MFC提供的AfxLoadLibrary显式的将自己所做的动态连接库调进来,并指定DLL的路径作为参数。LoadLibary返回HINSTANCE参数,应用程序在调用GetProcAddress函数时使用这一参数。当完成对动态链接库的导入以后,再使用GetProcAddress()获取想要引入的函数,该函数将符号名或标识号转换为DLL内部的地址,之后就可以象使用本应用程序自定义的函数一样来调用此引入函数了。在应用程序退出之前,应该用FreeLibrary或MFC提供的AfxFreeLibrary释放动态连接库。
隐式的调用
这种调用方式需要把产生动态连接库时产生的.LIB文件加入到应用程序的工程中,在使用DLL中的函数时,只须说明一下后就可以直接通过函数名调用DLL的输出函数,调用方法和程序内部其他的函数是一样的。隐式调用不需要调用LoadLibrary()和FreeLibrary()。程序员在建立一个DLL文件时,链接程序会自动生成一个与之对应的LIB导入文件。该文件包含了每一个DLL导出函数的符号名和可选的标识号,但是并不含有实际的代码。LIB文件作为DLL的替代文件被编译到应用程序项目中。
当程序员通过隐式调用方式编译生成应用程序时,应用程序中的调用函数与LIB文件中导出符号相匹配,这些符号或标识号被写入到生成的EXE文件中。LIB文件中也包含了对应的DLL文件名(但不是完全的路径名),链接程序也将其存储在EXE文件内部。当应用程序运行过程中需要加载DLL文件时,Windows根据这些信息发现并加载DLL,然后通过符号名或标识号实现对DLL函数的动态链接。所有被应用程序调用的DLL文件都会在应用程序EXE文件加载时被加载在到内存中。
隐式链接要链接相应的lib文件,在调用程序中,要包含对应dll文件的头文件(该头文件有相应函数和类的说明),不需要有windows.h头文件。