首页 > 代码库 > 01 动态链接库基础

01 动态链接库基础

DLL是一个包含函数和数据的模块, 它们可以被其他模块(应用程序或DLL)使用。

DLL可以定义两种函数: <1>导出函数   <2>内部函数 .

导出函数可以被内部或其他模块调用。

内部函数只能在DLL内部被调用。

 

  1. About Dynamic-Link Libraries

动态连接 允许一个模块在加载或运行时,仅仅只需包含定位一个动态库导出函数的信息,而无需将这个动态库整个编译进模块中。

 

调用一个DLL中的函数,有两种方法:

<1> load-time dynamic linking.(加载时动态链接), 是指显示地调用动态库中导出函数,就像这些函数在本地一样。  这需要你将模块与DLL对应的导入库文件进行链接。

 

<2> run-time dynamic linking(运行时动态链接), 在运行时使用LoadLibrary或者LoadLibraryEx函数来加载动态库。 当模块被加载后,使用GetProcAddress函数来获取动态库中导出的函数的地址。

 

DLL和内存管理:

每个进程将DLL加载到它的虚拟地址空间, 当DLL被加载到进程的虚拟地址空间后,他就可以调用DLL的导出函数了。

 

系统为每个引用的DLL维护一个引用计数,当一个线程加载DLL,引用计数会加1,当进程结束,引用计数减1,,  当引用计数为0时,DLL会被从虚拟地址空间中卸载。

 

与其他函数一样,一个导出的DLL函数运行在调用它的线程的上下文中,所以:

<1>进程中调用DLL的线程可以使用 被DLL函数打开的句柄,相似地,由线程打开的句柄也可以被DLL函数使用。

<2>DLL使用调用它的线程的栈, 使用调用它的进程的虚拟地址空间。

<3>DLL从调用它的进程的虚拟地址空间中分配内存。

 

动态链接库的优点:(相对于静态链接库)

<1>节省内存。 当多个进程加载相同的DLL时,DLL只会在物理内存中加载一份DLL, 每个进程加载DLL在物理内存中同一个基地址。  

<2>当DLL的函数的实现改变,而函数声明没变的情况下,从新生成DLL。应用程序不需要重新编译和链接。

<3>不同编程语言写的程序可以调用相同的DLL函数,只要遵循调用约定。

调用约定(C , Pascal , 或者标准调用) 控制调用函数参数的压栈顺序,是否调用函数,还是被调用函数负责清理栈,是否任何参数被传递到寄存器中等。

 

使用动态库的潜在的缺点是: 引用程序不是自包含的: DLL依赖于DLL的存在。

当使用load-time dynamic linking, 如果应用程序需要的DLL不存在,那么系统将结束这个进程。

当使用run-time dynamic linking, 如果需要的DLL不存在,系统不会结束该进程,但是DLL导出还是不可用。

 

创建动态链接库

 

线程安全: 如果你的DLL被多线程应用程序使用,那必须对所有DLL数据进行同步,以避免数据污染。

 

如果要使用load-time dynamic linking, 必须创建一个导入库(.lib文件).

 

导出函数的两个方法:

<1>使用 __declspec 修饰符

<2>使用.def文件

 

创建导入库: .lib文件。   如何创建导入库?

 

动态链接库的入口点函数

动态库可以可选地指定一个入口点函数。 如果指定了,当一个进程或线程加载或卸载动态库时,系统都会执行入口点函数。 它可以被用来执行一些初始化和清理任务。

DllMain是一个占位符,当创建自己的DLL时,必须指定实际的入口点函数名字。

入口函数定义:DLL入口点函数必须以标准调用约定(standard-call calling convention)定义


DLL的导入库(即与dll文件相对应的.lib文件),提供系统加载动态库所需要的信息;并且提供定位动态库中的导出函数信息。