dll的创建和调用

动态链接库英文为DLL,是Dynamic Link Library的缩写。dll是一个被其他应用程序调用的程序模块,其中封装了可以被调用的资源或函数。它是依附于EXE文件创建的的进程来执行的,不能够单独运行。每个程序都可以通过包含dll使用dll中包含的功能,这有助于避免代码重用和促进内存的有效使用。 通过使用 DLL,程序可以实现模块化,由相对独立的组件组成。因为模块是彼此独立的,所以程序的加载速度更快,而且模块只在相应的功能被请求时才加载。
比喻就是静态库就是你留在这别走了,而动态库就是你要帮忙时叫我一下,我再来。

新建dll

  1. 打开Visual Studio,新建 -> 项目 -> C++ -> 动态链接库
    dll的创建和调用_第1张图片
  2. 添加头文件myDll.h在头文件中添加导出函数add函数
#pragma once
//定义导出函数的接口声明
_declspec(dllexport) int _stdcall add(int a, int b);
  1. 添加源文件myDll.cpp在源文件添加函数的定义
//填写mfc dll函数的实现
int _stdcall add(int a, int b)
{
	//用于模式切换时的状态保护 最好加上这一句
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	//定义函数体
	//输出一个mfc消息框
	AfxMessageBox(L"hello i am mfc dll");
	return a + b;
}
  1. 编写好之后,我们直接右键项目名,点生成就可以项目路径下的debug文件夹里找到我们生成的dll和lib文件(注意编译方式的统一)
    在这里插入图片描述
  2. 也可以使用模块定义文件(.def)导出dll
; MFCLibrary1.def : 声明 DLL 的模块参数。

LIBRARY

EXPORTS
    ; 此处可以是显式导出
	add @1

LIBRARY后跟的是项目名,EXPORTS后是这个dll中需要导出的函数名
模块定义文件(.def)是包含一个或多个描述DLL各种属性的模块语句的文本文件,DEF文件必须至少包含下列模块定义语句:(1)文件中的第一个语句必须是LIBRARY语句。(2)EXPORTS语句列出名称,可能的话还会列出DLL导出函数的序号值。通过在函数名的后面加上@符和一个数字,给函数分配序号值。当指定序号值时,序号值的范围必须是从1到N,其中N是DLL导出函数的个数。

调用dll

隐式调用(静态调用)

将所需的.dll .lib .h放到工程目录下

//隐式加载dll
#pragma comment(lib, "dllDemo.lib")
extern "C" __declspec(dllimport) int __stdcall add(int a, int b);
void main()
{
	int sum = add(10, 20);
	printf("静态调用,sum = %d\n", sum);
}

或者在项目->属性->链接器->输入中添加附加依赖项 dllDemo.lib。
编译运行,该DLL会直接执行其中的可执行代码

显示调用(动态调用)

DLL显式加载时指在程序运行过程中,需要用到dll里的函数时,再动态加载dll到内存中,这种加载方式因为是在程序运行后再加载的,所以可以让程序启动更快,而且dll的维护更容易,使得程序如果需要更新,很多时候直接更新dll,而不用重新安装程序.只是这种加载方式,函数调用稍微复杂一点。
需要注意的时这种方式下调用相比较为复杂。比如之前文章中的调用带界面的DLL时,

//1.加载动态库
HINSTANCE hInst= LoadLibrary(L"MyDll.dll");
if (hInst == NULL)
{
	printf("加载动态库失败\n");
	FreeLibrary(hInst );
	return;
}
//2.根据函数名获取函数地址
typedef void(*Add)();//函数指针
Add PrintHello = (Add)GetProcAddress(hInst, "PrintHello");//从dll中加载函数出来
if (PrintHello == NULL)
{
	FreeLibrary(hInst);
	AfxMessageBox("函数地址获取失败");
}
PrintHello();//运行函数
FreeLibrary(hInst);       //LoadLibrary后要记得FreeLibrary
system("pause");
return 0;
// 运行时加载DLL库
HMODULE module = LoadLibraryA("DLLTest1.dll");     // 根据DLL文件名,加载DLL,返回一个模块句柄
if (module == NULL)
{
	printf("加载动态库失败\n");
	FreeLibrary(module);
	return;
}
typedef int(*AddFunc)(int, int);                  // 定义函数指针类型
// 导出函数地址
AddFunc add = (AddFunc)GetProcAddress(module, "add");// GetProcAddress返回指向的函数名的函数地址
if (add == NULL)
{
	FreeLibrary(module);
	AfxMessageBox("函数地址获取失败");
}
int sum  = add(100, 200);
printf("动态调用,sum = %d\n",sum);

Steps:
1、声明头文件,说明我想用windows32方法来加载和卸载DLL
2、然后用typedef定义一个指针函数类型.typedef void(*Add) //这个指针类型,要和你调用的函数类型和参数保持一致
3、定一个句柄实例,用来取DLL的实例地址。HINSTANCE hInst
格式为hInst=LoadLibrary(“DLL地址”);这里字符串类型是LPSTR,当是unicode字符集的时候会不行,
因此要在配置-属性-常规里面把默认字符集“unicode”改成支持多字符扩展即可。
4、取的地址要判断,返回的句柄是否为空,如果为无效句柄,那么要释放加载DLL所占用的内存。
5、定义一个函数指针,用来获取你要用的函数地址。
然后通过GetProcAdress来获取函数的地址,参数是DLL的句柄和你要调用的函数名:比如:add=(Add)GetProcAdress(hdll,"add");
这里也要判断要函数指针是否为空,如果没取到要求的函数,那么要释放句柄。
6、然后通过函数指针来调用函数。
7、调用结束后,就释放句柄FreeLibrary(hInst);

扩展

预编译 --> 编译 --> 汇编 --> 链接

  • 预编译,即预处理,主要处理在源代码文件中以“#”开始的预编译指令,如宏展开、处理条件编译指令、处理#include指令等。
  • 编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析以及优化后生成相应的汇编代码文件。
  • 汇编是将汇编代码转变成二进制文件。
  • 链接将二进制文件链接成一个可执行的命令,主要是把分散的数据和代码收集并合成一个单一的可加载并可执行的的文件。链接可以发生在代码静态编译、程序被加载时以及程序执行时。链接过程的主要工作是符号解析和重定位。

你可能感兴趣的:(mfc,c++,windows)