1.什么是动态链接库
动态链接库(Dynamic-Link Library)通常包含程序员自定义的变量和函数,可以在运行时动态链接到可执行文件中。
静态链接与动态链接的区别:
静态链接是指在编译的时候就把模块的内容加载进来一起编译,这样编出来的exe文件包含了模块的内容,因此会比较大。而动态链接是指程序在运行时去加载模块中所需要的函数,exe文件与模块文件是分离的。一般来说,静态链接用的是lib文件,动态链接用的是dll文件。
如何生成lib和dll文件呢?以VC为例,先创建对应的工程(lib或dll)。
如果是lib,给每个需要导出的函数前加上extern修饰就行了,像这样:
extern void my_function() {...}
如果是dll,则在函数前加上__declspec(dllexport),如
__declspec(dllexport) void my_function{...}
使用lib很简单,首先在工程的链接选项里加入你要链接的lib文件,然后在你想用lib里面的函数时,用extern声明一下就行了,比如extern void my_function(),如果能获得头文件,直接#include "xxx.h"即可,这样编译器遇到my_function这个函数时就会在你指定的lib中查找。另外需要提一下的是,如果lib里面的函数是c写的,那么在c++中使用这些函数的时候需要这样声明:
extern "C" {extern void my_function()}
这是因为c和c++在生成lib时对函数的命名规则不同,使用extern "C"是告诉编译器要按照C的命名规则来查找函数。
使用dll也很简单,不过有两种使用方式。
一种是使用LoadLibrary在运行时加载dll,然后获得函数的地址再使用。如果我们要大量使用该dll里面的函数时,这种用法是非常不方便的,能不能像使用lib一样使用dll呢?答案是肯定的。dll工程在生成dll文件的同时还会生成一个同名的lib文件,这个lib文件和上面说的lib文件是不同的,可以看作是一个对生成的dll进行描述的文件。我们只要像使用lib一样把dll的lib文件加到工程中,就可以像静态链接那样使用dll里面的函数了(还是需要用extern声明)。
2.动态库扩展名
Windows下是.dll,Linux下是.so(share object)
3.Windows系统动态链接库
Windows操作系统核心有三个动态链接库(Kernel32.dll、User32.dll、Gdi32.dll),这些动态链接库构成了Win32 API函数
4.动态链接库的优点
1)节省内存和代码重用:当应用程序使用动态链接时,多个应用程序可以共享磁盘上单个DLL副本。
2)可扩展性:DLL文件与EXE文件独立,只要接口不变,升级程序只需更新DLL文件不需要重新编译应用程序。
3)复用性:DLL的编制与具体的编程语言以及编译器无关,不同语言编写的程序只要按照函数调用约定就可以调用同一个DLL函数。
5.动态链接库的缺点
DLL地狱(DLL HELL)
6.DLL创建方法
1、打开VS2010,点击文件->新建->项目
2、点击已安装模版->Visual C++->Win32项目,填写名称及解决方案名称
3、点击下一步,应用程序选择DLL,附加选项选择空项目
4、将生成一个空工程,在头文件添加新建项选择头文件(.h),输入名称testdll,源文件选择添加新建项选择C++文件(.CPP),输入名称testdll
5、在testdll.h文件填写下面代码:
namespace MathFuncs
{
class MyMathFuncs
{
public:
__declspec(dllexport)double Add(doublea,double b);
__declspec(dllexport)double Subtract(doublea,double b);
__declspec(dllexport)double Multiply(doublea,double b);
__declspec(dllexport)double Divide(doublea,double b);
};
}
或者
namespace MathFuncs
{
class __declspec(dllexport) MyMathFuncs
{
public:
double Add(double a,double b);
double Subtract(doublea,double b);
double Multiply(doublea,double b);
double Divide(doublea,double b);
};
}
上述两种方式的区别在于第一种可以指定类中那些成员函数可以被其他程序调用,第二种方式类中所有成员函数都可以被其他程序调用
6、在testdll.cpp文件里面添加如下代码:
// testdll.cpp
#include "testdll.h"
namespace MathFuncs
{
double MyMathFuncs::Add(doublea,double b)
{
return a + b;
}
double MyMathFuncs::Subtract(doublea,double b)
{
return a - b;
}
double MyMathFuncs::Multiply(doublea,double b)
{
return a * b;
}
double MyMathFuncs::Divide(doublea,double b)
{
return a / b;
}
}
7、点击生成解决方案,转到工程目录下可以看到在Debug目录下有testdll.lib和testdll.dll两个文件,在其他项目里面我们要用到的三个文件testdll.lib,testdll.dll和testdll.h
7.DLL使用
下面实现两种调用方式:单独.dll 和.h + .lib + .dll结合
注:需把对应的 .dll 文件以及.lib 文件和.h文件(结合方式时)拷贝至调用的程序目录下
1、新建一个空工程,添加源文件mian.cpp,在里面添加如下代码:
(1)单纯使用.dll
#include<wtypes.h>
#include <winbase.h>
#include <iostream>
_declspec(dllimport) int Add(int a, int b); //导入声明,亦可以不加,如果加上可加快程序运行
typedef int(*pAdd)(int a,int b);
int main()
{
HINSTANCE hDLL;
pAdd Add;
hDLL=LoadLibrary(L"mydll.dll"); //加载 DLL文件
if(hDLL == NULL)std::cout<<"Error!!!\n";
Add=(pAdd)GetProcAddress(hDLL,"add"); //取DLL中的函数地址,以备调用
int a =Add(5,8);
std::cout<<"a: "<<a<<std::endl;
FreeLibrary(hDLL);
return 0;
}
输出结果:
(2).h + .lib + .dll 结合方式
.h头文件是编译时必须的,lib是链接时需要的,dll是运行时需要的。
.h .lib .dll三者的关系是:
H文件作用是:声明函数接口
DLL文件作用是:函数可执行代码
当我们在自己的程序中引用了一个H文件里的函数,编链器怎么知道该调用哪个DLL文件呢?这就是LIB文件的作用:告诉链接器,调用的函数在哪个DLL中,函数执行代码在DLL中的什么位置,这也就是为什么需要附加依赖项 .LIB文件,它起到桥梁的作用。如果生成静态库文件,则没有DLL ,只有lib,这时函数可执行代码部分也在lib文件中。
dumpbin /exports d:\testdll.bin 可以查看dll导出函数
#include<wtypes.h>
#include <winbase.h>
#include <iostream>
#include "../MyDll/mydll.h"
#pragma comment(lib,"mydll.lib") //将mydll.lib库文件连接到目标文件中(即本工程)
extern "C"_declspec(dllimport) int add(int a,int b);
int main()
{
int a =add(5,8);
std::cout<<"a: "<<a<<std::endl;
return 0;
}
此时如果去掉 .dll 文件(即只有.lib 和 .h文件),则会出错:
或者:
1、新建一个空工程,添加源文件mian.cpp,在里面添加如下代码:
#include <stdio.h>
#include <Windows.h>
#include "testdll.h"
using namespace MathFuncs;
void main()
{
MyMathFuncs st;
double c=st.Add(3.0,5.0);
printf("%f\n",c);
system("pause");
}
或者
#include <stdio.h>
#include <Windows.h>
#include "testdll.h"
void main()
{
double c=MathFuncs::MyMathFuncs::Add(3.0,5.0);
printf("%d\n",c);
system("pause");
}
第二种方法需要在类的定义中使用静态函数
static __declspec(dllexport)double Add(double a,doubleb);
否则需要实例化一个类的对象再调用类中函数
2、将testdll.lib和testdll.h放到工程默认目录下,即CPP文件所在的目录,点击项目->属性->配置属性->链接器->输入->附加依赖项,填写testdll.lib,若把testdll.lib和testdll.h放到Debug目录下,则填写..\Debug\testdll.lib
3、将testdll.dll放入cpp文件所在位置或者Debug目录下都可以,若把testdll.h文件放在其他地方则在头文件地方手动添加进来即可,或者点击项目->属性->配置属性->C/C++->常规->附加包含目录,添加头文件所在目录
注:若提示DLL文件读取错误而路径包含等都没有问题,则可以删除附加依赖项,在程序头加入#pragmacomment(lib , "testdll.lib")即可。