DLL 是 Dynamic Link Library 的缩写,译为“动态链接库”。DLL也是一个被编译过的二进制程序,DLL 中封装了很多函数,只要知道函数的入口地址,就可以被其他程序调用。但与 exe 不同,DLL不能独立运行,必须由其他程序调用载入内存。
Windows API中所有的函数都包含在DLL中,其中有3个最重要的DLL:
Kemel32.dll:它包含那些用于管理内存、进程和线程的函数,例如CreateThread函数;
User32.dll:它包含那些用于执行用户界面任务(如窗口的创建和消息的传送)的函数,例如 CreateWindow 函数;
GDI32.dll:它包含那些用于画图和显示文本的函数。
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
静态链接库和动态链接库
1) 静态库
函数和数据被编译进一个二进制文件(通常扩展名为.LIB)。在使用静态库的情况下, 在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其他模块组合起来创建最终的可执行文件(.EXE文件)。当发布产品时,只需要发布这个可执行文件,并不需要发布被使用的静态库。
2) 动态库
在使用动态库的时候,往往提供两个文件:一个引入库(.lib)文件和一个DLL (.dll) 文件。虽然引入库的后缀名也是“lib”,但是,动态库的引入库文件和静态库文件有着本质上的区别,对一个DLL来说,其引入库文件(.lib)包含该DLL导出的函数和变量的符号名,而.dll文件包含该DLL实际的函数和数据。在使用动态库的情况下,在编译链接可执行文件时,只需要链接该DLL的引入库文件,该DLL中的函数代码和数据并不复制到可执行文件中,直到可执行程序运行时,才去加载所需的DLL,将该DLL映射到进程的地址空间中,然后访问DLL中导出的函数。这时,在发布产品时,除了发布可执行文件以外,同时还要发布该程序将要调用的动态链接库。
使用动态链接库的好处
1) 可以采用多种编程语言来编写
我们可以采用自己熟悉的开发语言编写DLL,然后由其他语言编写的可执行程序来调用这些DLL。例如,可以利用VB来编写程序的界面,然后调用利用VC++或Delphi编写的完成程序业务逻辑的DLL。
2) 增强产品的功能
在发布产品时,可以发布产品功能实现的动态链接库规范,让其他公司或个人遵照这个规范开发自己的DLL,以取代产品原有的DLL,让产品调用新的DLL,从而实现功能 的增强。在实际工作中,我们看到许多产品都提供了界面插件功能,允许用户动态地更换程序的界面,这就可以通过更换界面DLL来实现。
3) 提供二次开发的平台
在销售产品的同时,可以采用DLL的形式提供一个二次开发的平台,让用户可以利用该DLL调用其中实现的功能,编写符合自己业务需要的产品,从而实现二次开发。
4) 简化项目管理
在一个大型项目开发中,通常都是由多个项目小组同时开发,如果采用串行开发,则效率是非常低的。我们可以将项目细分,将不同功能交由各项目小组以多个DLL的方式实现,这样,各个项目小组就可以同时进行开发了。
5) 可以节省磁盘空间和内存
如果多个应用程序需要访问同样的功能,那么可以将该功能以DLL的形式提供,这样在机器上只需要存在一份该DLL文件就可以了,从而节省了磁盘空间。另外,如果多个应用程序使用同一个DLL,该DLL只需要放入内存一次,所有的应用程序就都可以共亨它了。这样,内存的使用将更加有效。
我们知道,当进程被加载时,系统会为它分配内存,接着分析该可执行模块,找到该程序将要调用哪些DLL,然后系统搜索这些DLL,找到后就加载它们,并为它们分配内存空间。DLL的内存空间只有一份,如果有第二个程序也需要加载该DLL,那么它们共享内存空间,相同的DLL不会再次加载。
6) 有助于资源的共享
DLL可以包含对话框模板、字符串、图标和位图等多种资源,多个应用程序可以使用DLL来共享这些资源。在实际工作中,可以编写一个纯资源的动态链接库,供其他应用程序访问。
7) 有助于实现应用程序的本地化
如果产品需要提供多语言版本,那么就可以使用DLL来支持多语言。可以为每种语言创建一个只支持这种语言的动态链接库。
1、打开VS2013,创建项目,点DLL,输入项目名称,MakeDll,添加新项,来创建头文件MakeDll.h;
MakeDll.h
- #define DLL_API __declspec(dllexport)
- #include<iostream>
- using namespace std;
- DLL_API int add(int a, int b);
- class DLL_API Point
- {
- private:
- float x, y;
- public:
- Point();
- void SetPoint(float a, float b);
- void DisPlay();
- };
#define DLL_API __declspec(dllexport)
#include<iostream>
using namespace std;
DLL_API int add(int a, int b);
class DLL_API Point
{
private:
float x, y;
public:
Point();
void SetPoint(float a, float b);
void DisPlay();
};
2、创建MakeDll.cpp来实现MakeDll.h中的函数和类;
在MakeDll.cpp中需要包含MakeDll.h头文件==>右击项目==>属性==>配置属性==>VC++目录==>可执行文件目录,在项目中找到MakeDll.h所在目录,包含以下就可以了
MakeDll.cpp
- #include<MakeDll.h>
- DLL_API int add(int a, int b)
- {
- return a + b;
- }
- Point::Point()
- {
- x = 0.0f;
- y = 0.0f;
- }
- void Point::SetPoint(float x, float y)
- {
- this->x = x;
- this->y = y;
- }
- void Point::Display()
- {
- cout << "x= " << x << endl;
- cout << "y= " << y << endl;
- }
#include<MakeDll.h>
DLL_API int add(int a, int b)
{
return a + b;
}
Point::Point()
{
x = 0.0f;
y = 0.0f;
}
void Point::SetPoint(float x, float y)
{
this->x = x;
this->y = y;
}
void Point::Display()
{
cout << "x= " << x << endl;
cout << "y= " << y << endl;
}
3、生成==>生成解决方案
在MakeDll项目所在目录下的Debug目录下的文件有MakeDll.dll和MakeDll.lib了
4、新建项目==>win32控制台应用程序,项目名称:UsingDll,确定==>下一步,勾上空项目;第一个项目中生成的MakeDll.dll和MakeDll.lib复制到 UsingDll\UsingDll目录下将项目MakeDll中的MakeDll.h头文件也复制到本项目中,最好复制到 UsingDll \UsingDll目录中然后同样的,右击项目==>属性,如同上面一样把MakeDll.h所在目录包含一下;
5、新建一个UsingDll.cpp;
- #include<MakeDll.h>
- #define DLL_API __declspec(dllimport)
- #pragma comment(lib,"MakeDll.lib")
- int main()
- {
- Point p1, p2;
- p2.SetPoint(5.6f, 7.8f);
- p1.Display();
- p2.Display();
- cout << "5+6 = " << add(5, 6) << endl;
- return 0;
- }
#include<MakeDll.h>
#define DLL_API __declspec(dllimport)
#pragma comment(lib,"MakeDll.lib")
int main()
{
Point p1, p2;
p2.SetPoint(5.6f, 7.8f);
p1.Display();
p2.Display();
cout << "5+6 = " << add(5, 6) << endl;
return 0;
}
6、运行成功;
================================================================
在vs2013中如何添加c/c++工程中外部头文件及库的基本步骤:
1、添加工程的头文件目录:工程---属性---配置属性---c/c++---常规---附加包含目录:加上头文件存放目录。
2、添加文件引用的lib静态库路径:工程---属性---配置属性---链接器---常规---附加库目录:加上lib文件存放目录。
然后添加工程引用的lib文件名:工程---属性---配置属性---链接器---输入---附加依赖项:加上lib文件名。
3、添加工程引用的dll动态库:把引用的dll放到工程的可执行文件所在的目录下。
无法打开文件“MakeDll.lib"
解决:
#pragma comment(lib,"MakeDll.lib")
================================================================
下面看一段关于静态库和动态库插曲:
库是写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。
本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库有两种:静态库(.a、.lib)和动态库(.so、.dll)。
所谓静态、动态是指链接。回顾一下,将一个程序编译成可执行程序的步骤:
图:编译过程
之所以成为【静态库】,是因为在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。
试想一下,静态库与汇编生成的目标文件一起链接为可执行文件,那么静态库必定跟.o文件格式相似。其实一个静态库可以简单看成是一组目标文件(.o/.obj文件)的集合,即很多目标文件经过压缩打包后形成的一个文件。静态库特点总结:
l 静态库对函数库的链接是放在编译时期完成的。
l 程序在运行时与函数库再无瓜葛,移植方便。
l 浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。
下面编写一些简单的四则运算C++类,将其编译成静态库给他人用,头文件如下所示:
StaticMath.h头文件 |
#pragma once class StaticMath { public: StaticMath(void); ~StaticMath(void);
static double add(double a, double b);//加法 static double sub(double a, double b);//减法 static double mul(double a, double b);//乘法 static double div(double a, double b);//除法
void print(); }; |
Linux下使用ar工具、Windows下vs使用lib.exe,将目标文件压缩到一起,并且对其进行编号和索引,以便于查找和检索。一般创建静态库的步骤如图所示:
图:创建静态库过程
Linux下创建与使用静态库
Linux静态库命名规则
linux静态库命名规范,必须是"lib[your_library_name].a":lib为前缀,中间是静态库名,扩展名为.a。
创建静态库(.a)
通过上面的流程可以知道,Linux创建静态库过程如下:
l 首先,将代码文件编译成目标文件.o(StaticMath.o)
g++ -c StaticMath.cpp |
注意带参数-c,否则直接编译为可执行文件
l 然后,通过ar工具将目标文件打包成.a静态库文件
ar -crv libstaticmath.a StaticMath.o |
生成静态库libstaticmath.a。
大一点的项目会编写makefile文件(CMake等等工程管理工具)来生成静态库,输入多个命令太麻烦了。
使用静态库
编写使用上面创建的静态库的测试代码:
测试代码: |
#include "StaticMath.h" #include <iostream> using namespace std;
int main(int argc, char* argv[]) { < |