一、DLL
动态链接库英文为DLL,是Dynamic Link Library的缩写。DLL是一个包含可由多个程序,同时使用的代码和数据的库。
- 通过使用 DLL,程序可以实现模块化,由相对独立的组件组成,因为模块是彼此独立的,所以程序的加载速度更快,而且模块只在相应的功能被请求时才加载。
- 动态链接与静态链接的不同之处在于:动态链接允许可执行模块(.dll 文件或 .exe 文件)仅包含在运行时定位 DLL 函数的可执行代码所需的信息。在静态链接中,链接器从静态链接库获取所有被引用的函数,并将库同代码一起放到可执行文件中。
- 使用动态链接代替静态链接有若干优点。DLL 节省内存,减少交换操作,节省磁盘空间,更易于升级,提供售后支持,提供扩展 MFC 库类的机制,支持多语言程序,并使国际版本的创建轻松完成。
- 调用方式主要分为两种:隐式(三件套-.h.lib.dll) 与 显式(DLL文件)。
二、隐式调用(三步法)
隐式调用三要素,.h文件 ,.dll文件,.lib文件,缺一不可。
步骤:一创二引三调用
1.创建DLL
vs2015:新建=》项目=》Win32控制台程序=》项目名“myDll”=》确定=》下一步=》选择DLL=》勾选空白项=>完成。
在项目的头文件里添加myDLL.h,在源文件里添加myDLL.cpp,并写入如下代码:
//myDLL.h
#include <iostream>
using namespace std;
#ifdef MYDLL_API
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport) //当编译时,头文件不参加编译,所以.cpp文件中先定义,后头文件被包含进来,因此外部使用时,为dllexport,而在内部编译时,则为dllimport
#endif
namespace myPro
{
class MYDLL_API Person
{
public:
void show();
Person(char*, int);
~Person();
private:
char* m_name;
int m_age;
};
MYDLL_API void print();
MYDLL_API int add(int a, int b);
MYDLL_API void swap(float &a, float &b);
}
//myDLL.cpp
#include "myDLL.h"
namespace myPro
{
Person::Person(char* name, int age) :m_name(name), m_age(age) {}
Person::~Person() {}
void Person::show()
{
cout<<"name = "<< m_name;
cout<< " age = " << m_age << endl;
}
void print()
{
cout << "Hello DLL!!!" << endl;
}
int add(int a, int b)
{
return a + b;
}
void swap(float &a, float &b)
{
float temp = a;
a = b;
b = temp;
}
}
项目名上右键=》生成。\myDll\Debug目录下就会生成dll,lib。
2.引用DLL
在同一个解决方案下(也可以在不同的解决方案下)创建调用dll的Win32控制台程序:解决方案名上右键=》添加=》新建项目=》Win32控制台应用程序=》项目名为“useDLL”=>确定=》勾选空项目=》完成。
引用三个文件:.lib .h .dll
选中useDLL项目=》项目=》属性。
- 包含目录:添加.h所在的文件夹
- 库目录:添加。lib所在的文件夹
- 链接器=》输入=》附加依赖项中添加myDll.lib=>并且确定。
3 调用
在useDLL.cpp引入头文件,并使用DLL中的内容。
#include <iostream>
#include "myDLL.h"
using namespace std;
using namespace myPro;
int main()
{
char* name = "Heiren";
Person p(name, 12345);
p.show();
int a1 = 12, a2 = 21;
cout << "a1 + a1 = " << add(a1, a2) << endl;
float f1 = 1.2, f2 = 3.4;
myPro::swap(f1, f2);
cout << "f1 = " << f1 << " f2 = " << f2 << endl;
system("pause");
return 0;
}
三、显式调用
显示调用只需要一个dll文件。显示调用好处是模块相对独立,更新非常方便。不好的地方是使用起来稍微复杂。感觉显式调用不常用,所以这里对调用函数简单介绍一下。
- 显示调用dll中的类好像比较繁琐,可以参考https://www.cnblogs.com/fer-team/archive/2017/12/13/8033538.html
新建一个dll项目,生成dll,把dll放在调用它的win32应用程序同目录下。
//d111.cpp
extern "C" __declspec(dllexport) const char* print()
{
return "hello,DLL!!!";
}
//main.cpp
#include <Windows.h>
#include <iostream>
using namespace std;
typedef const char*(*testFunc)();
int main()
{
HINSTANCE hDll = LoadLibraryA("myDll2.dll");
testFunc tf = (testFunc)GetProcAddress(hDll, "print");
if (!tf)
cout << "Error" << endl;
else
cout << tf() << endl;
FreeLibrary(hDll);
system("pause");
return 0;
}
输出结果
四 总结
动态链接具有下列优点:
- 节省内存和减少交换操作。很多进程可以同时使用一个 DLL,在内存中共享该 DLL 的一个副本。相反,对于每个用静态链接库生成的应用程序,Windows 必须在内存中加载库代码的一个副本。
- 节省磁盘空间。许多应用程序可在磁盘上共享 DLL 的一个副本。相反,每个用静态链接库生成的应用程序均具有作为单独的副本链接到其可执行图像中的库代码。
- 升级到 DLL 更为容易。当 DLL 中的函数发生更改时,只要函数的参数和返回值没有更改,就不需重新编译或重新链接使用它们的应用程序。相反,静态链接的对象代码要求在函数更改时重新链接应用程序。
- 提供售后支持。例如,可修改显示器驱动程序 DLL 以支持当初交付应用程序时不可用的显示器。
- 支持多语言程序。只要程序遵循函数的调用约定,用不同编程语言编写的程序就可以调用相同的 DLL 函数。程序与 DLL 函数在下列方面必须是兼容的:函数期望其参数被推送到堆栈上的顺序,是函数还是应用程序负责清理堆栈,以及寄存器中是否传递了任何参数。
- 提供了扩展 MFC 库类的机制。可以从现有 MFC 类派生类,并将它们放到 MFC 扩展 DLL 中供 MFC 应用程序使用。
- 使国际版本的创建轻松完成。通过将资源放到 DLL 中,创建应用程序的国际版本变得容易得多。可将用于应用程序的每个语言版本的字符串放到单独的 DLL 资源文件中,并使不同的语言版本加载合适的资源。
- 使用 DLL 的一个潜在缺点是应用程序不是独立的;它取决于是否存在单独的 DLL 模块。