动态链接库
动态链接库
动态链接库的概述
DLL就是真个windows操作系统的基础。动态链接库不能直接运行,也不能接收消息。他们是一些独立的文件
Windows API中的所有函数都包含在DLL中
其中最重要的DLL
- Kernel32.dll,它包含用于管理内存、进程和线程的各个函数:CreateThread
- User32.dll,它包含用于执行用户界面任务(如窗口的创建和消息的传送)的各个函数
- GDI32.dll,它包含用于画图和显示文本的各个函数。
- Windbg 是个APP
静态库和动态库
- 静态库:函数和数据被编译进一个二进制文件(通常扩展名为**.LIB**)。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把他们和应用程序的其他模块组合起来创建最终的可执行文件(.EXE文件);
- 在使用动态库的时候,往往提供两个文件:一个因入库和一个DLL。因入库包含被DLL导出的函数和变量的符号名,DLL包含实际的函数和数据。在编译链接可执行文件时,只需要链接引入库,DLL中的函数代码和数据并不复制到可执行文件中,在运行时候,再去加载DLL,访问DLL中导出的函数。
使用动态链接库的好处
增强产品的功能。(更换界面的DLL)换皮肤
提供二次开发的平台。(SDK基础版本)
简化项目管理(串行开发,以多个DLL的方式实现)
可以节省磁盘空间和内存
有助于资源的共享。(对话框模板,字符串,图标)
有助于实现应用程序的本地化。(多语言版本)
可以采用多种编程语言来编写
VB delphi写界面,然后调用MFC编写的DLL来完成业务开发
动态链接库的使用
DumpBin命令
建立DLL文件代码如下
Dll1.cpp
#include "pch.h"
// 当使用预编译的头时,需要使用此源文件,编译才能成功。
//提供给用户用的函数
//应用程序如果要访问某个Dll中的函数,那么函数必须是被已经导出的函数
//为了让DLL导出一些函数,需要在每一个将要被导出的函数前面添加
//标识符:_declspec(dllexport) 必须带,以生成.lib文件
_declspec(dllexport) int add(int a, int b)
{
return a + b;
}
_declspec(dllexport) int substrcat(int a, int b)
{
return a - b;
}
如果要查出*.dll中包含信息,可在命令行下进入Debug所在目录,输入以下命令
dumpbin -exports dll.dll
有些时候由于某种安装原因,dumpbin被认为是无效命令,接下来在
C:\Program Files\Microsoft Visual Studio\VC14\Bin\下找到VCVARS32.bat
并在命令行运行,之后就能执行dumpbin命令了。
- 名字改编 === 名字粉碎
- 在exe运行的时候,系统将为exe分配一个4GB的地址空间,然后加载模块会分析该应用程序的输入信息,从中找到该程序将要访问的动态链接库信息。然后再用户机器上搜索这些动态链接库
MFC控件绑定
//用add和substract从外面导入的函数
//隐式链接 显示加载
extern int add(int a, int b);
extern int substrcat(int a, int b);
void CDllMFCTestDlg::OnBnClickedAddButton()
{
// TODO: 在此添加控件通知处理程序代码
CString str;
str.Format(L"4 + 3 = %d", add(4, 3));
MessageBox(str);
}
void CDllMFCTestDlg::OnBnClickedSubstractButton()
{
// TODO: 在此添加控件通知处理程序代码
CString str;
str.Format(L"4 - 3 = %d", substrcat(4, 3));
MessageBox(str);
}
为使编译器认识Add, Substract必须在之前使用两个声明:
extern int Add(int x,int y);
extern int Substract(int x,int y);
可以使用标识符表示这两个函数是从动态链接库的.lib文件引用的,以生成效率更高的代码
_declspec(dllimport) int Add(int x,int y);
_declspec(dllimport) int Subtract(int x,int y);
DLL的导入和导出以及条件编译
-
_declspec(dllexport)与_declspec(dllimport)
与使用extern关键字这种方式对比,使用_declspec(dllimport)
的标识符,他将告诉编译器是从动态链接库引入的_declspec(dllexport)
是 类、函数以及数据的声明的时候使用。把.dll里面的相关代码暴露出来给其他应用程序使用。**提供给别的应用程序使用。**标识提供者。供dll内部使用_declspec(dllimport)
是在外部程序需要使用DLL内相关内容使用的标识符。是把dll中的相关代码插入到应用程序中去。表示使用者,不是dll内部使用。
-
DLL实现之后,交给客户程序。Dumpbin
-
DLL的实现者和使用者不是同一个人。
- 方案:dll + 头文件的方式
- 头文件:建议使用条件指令编译。使得程序的可读性增强,灵活性增强。
- 方案:dll + 头文件的方式
-
条件指令编译
//如果定义了DLL1_API,则什么都不干,如果没有定义DLL1_API,则 //DLL1_API _declspec(dllimport) //条件编译指令 //#define DLL1_API _declspec(dllexport) #ifdef DLL1_API #else #define DLL1_API _declspec(dllimport) #endif // DLL1_API int add(int a, int b); DLL1_API int substrcat(int a, int b);
从DLL中导出C++类
动态链接库导出整个类和仅导出该类的某些成员函数在实现方式的区别:
如果在声明类时,制定了导出标志,那么该类中所有的函数都被导出;否则只有那些声明时指定了的导出标志的类成员函数才被导出
DLL1.cpp
void Point::output(int x, int y)
{
//返回调用者进程当前使用的窗口
HWND hWnd = GetForegroundWindow();
//获取DC
HDC hdc = GetDC(hWnd);
char buf[32] = { 0 };
sprintf_s(buf, "x = %d, y = %d", x, y);
//输出坐标
TextOutA(hdc, 0, 0, buf, strlen(buf));
//释放DC
ReleaseDC(hWnd,hdc);
}
void Point::test()
{
int x = 1, y = 2;
//返回调用者进程当前使用的窗口
HWND hWnd = GetForegroundWindow();
//获取DC
HDC hdc = GetDC(hWnd);
char buf[32] = { 0 };
sprintf_s(buf, "x = %d, y = %d", x, y);
//输出坐标
TextOutA(hdc, 50, 50, buf, strlen(buf));
//释放DC
ReleaseDC(hWnd, hdc);
}
Dll1.h
//class DLL1_API Point
//{
//public:
// void output(int x, int y);
// void test();
//};
class Point
{
public:
void DLL1_API output(int x, int y);
void test();
};