动态链接库(三)--动态链接库的使用

这篇具有很好参考价值的文章主要介绍了动态链接库(三)--动态链接库的使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

写在前面

本文示例基于上章的Dll1项目生成的动态链接库学习简单使用.

所需文件:因为上节的示例没有添加Dll1.h头文件,因此这里只需Dll1.dll,Dll1.lib
在本文中会添加Dll1.h头文件以优化动态链接库的创建.

既然要在项目中使用别人创建生成的dll, 那么首先得将dll加载到自己得项目中去才行.

这里有两种方式加载DLL到项目中:
① 隐式链接方式加载DLL
② 显式动态方式加载DLL

隐式链接方式加载DLL

大致步骤如下:
步骤如下:
(1) 在调用DLL的导出函数程序,需先声明即将要调用的外部函数, 使用extern关键字或标识符_declspec(dllimport)。注意区分_declspec(dllexport).

**_declspec(dllexport)**存在于动态链接库项目中的函数声明中,表明该函数是一个DLL导出函数.

**_declspec(dllimport)**存在于使用dll的项目中,表明该函数从一个dll中导入到本项目中.

一般一个dll会有一个对应的.h头文件来声明是导出还是导入函数(通过宏来区别,后续会在Dll1项目中完善),因为我们当前的Dll1项目没有.h头文件,因此需在使用Dll1.dll 导出的函数前声明该函数是导入函数.

例我们在同一个解决方案下新建Dll_test项目,并将该项目设置为启动项,然后隐式加载DLL:
动态链接库(三)--动态链接库的使用

(2) 然后配置该项目属性-》连接器-》输入-》依赖附加项-》编辑添加lib/Dll1.lib
注意这里的输入相对路径是.vcxproj文件所在目录,所以要加lib/
动态链接库(三)--动态链接库的使用

拷贝.lib 和 .dll 文件到该程序目录下,一般会在项目根目录下新建一个lib文件夹用来存放lib文件,dll文件一般放在存放exe的Debug目录下,这样发布软件的时候会把dll随exe一起发布(正好dll是必需的).
动态链接库(三)--动态链接库的使用
动态链接库(三)--动态链接库的使用
(3) 同样的也需将Dll1.dll拷贝到程序输出目录下。这里是输出exe文件的Debug目录:
动态链接库(三)--动态链接库的使用
编译运行DLL_test项目:
动态链接库(三)--动态链接库的使用

这样就是一个隐式加载dll, 并使用得示例了.

关于上面的第**(2)步,这里还有一种代替方案,就是通过#pragma comment** 来在代码中添加lib引入库文件来代替项目属性中得配置:
动态链接库(三)--动态链接库的使用
注意:这里需去掉项目属性-》链接器 –》附加依赖项中的lib/Dll1.lib

上面使用的是绝对路径,也可以使用相对路径,相对当前文件去找Dll1.lib,例当前想在main.cpp文件中导入lib,我们右键main.cpp打开文件所在路径:
动态链接库(三)--动态链接库的使用
动态链接库(三)--动态链接库的使用
可以看到lib目录和当前文件main.cpp在同一级目录下,所以我们可以直接这样:
动态链接库(三)--动态链接库的使用
再次编译运行, 结果同上:
动态链接库(三)--动态链接库的使用

显式动态方式加载DLL

不再 在链接阶段加载dll, 而是通过Windows库提供得API在代码中需要的时候动态的加载dll, 随即使用加载的dll中的导出函数, 使用完后释放对dll的引用.

动态加载的方式不再需要 lib导入库文件 和 .h头 文件,只需dll即可,通过下面三个函数加载、释放dll引用,获取dll中的导出函数.

LoadLibrary函数

函数原型如下:

HMODULE LoadLibrary( LPCTSTR lpFileName);

作用是指定的可执行模块映射到调用进程的地址空间.

LoadLibrary函数不仅能够加载DLL(.dll) ,还可以加载可执行模块(.exe). 一般来说,当加载可执行模块时,主要是为了访问该模块内的一些资源,例对话框资源、位图资源或图标资源等.

参数类型为LPCTSTR, 指向可执行模块的名称,既可以是一个.dll文件,也可以是一个.exe文件.

如果调用成功返回所加载的模块的句柄, 失败返回NULL, 因为是WinApi, 调用失败可以 GetLastError函数获取失败信息.

动态链接库(三)--动态链接库的使用
LoadLibrary是WinApi,因此需包含 windows.h
_T宏定义在tcha.h中,因此也需包含 tchar.h

当获取到动态链接库模块的句柄后,接下来就要想办法获取该动态链接库中导出函数的地址,可以通过GetProcAddress函数得到.

GetProcAddress函数

函数原型如下:

FARPROC GetProcAddress(HMODULE hModule, LPCSTR lpProcName);

参数hModule : 指定动态链接库模块的句柄,即LoadLibrary函数的返回值.
参数lpProcName :一个指向常量的字符指针,指定DLL导出函数的名字或名字的序号.

**注意:**如果该参数指定的是导出函数的序号,那么该序号必须在低位字中,高位字必须是0. 不过这里也会有相应的宏帮助我们构造, 后有介绍.

调用成功返回指定导出函数的地址,否则返回NULL.

动态链接库(三)--动态链接库的使用

编译运行发现这样获取函数指针失败:
动态链接库(三)--动态链接库的使用

这里查找原因, 找到Dll1.dll文件,在该目录下shift + 鼠标右键打开命令行窗口,使用dumpbin工具查看Dll1.dll内容:
动态链接库(三)--动态链接库的使用
可以看到已经发生名称改编了,而我们获取函数指针的GetProcAddress函数的第二个参数为“add”,Dll1.dll中没有add,只有?add@@YAHHH@Z,所以才返回NULL.

这里我们copy命令行窗口中的?add@@YAHHH@Z 和 ?subtract@@YAHHH@Z 代替add和subtract:
动态链接库(三)--动态链接库的使用

再次编译运行, 可以看到成功获取:
动态链接库(三)--动态链接库的使用

然后和此前一样使用即可:
动态链接库(三)--动态链接库的使用
动态链接库(三)--动态链接库的使用

动态链接中的问题

无论隐式还是显式加载,Dll1.dll都有发生名字改编,为什么隐式加载时可以直接使用函数名(add、subtract)调用呢?

以add函数为例,这里的解释是:
隐式加载时,通过函数名add调用时,因为是同一个编译器同一个调用约定,因此调用时编译器会自动将add 解释成改编后的名字: ?add@@YAHHH@Z,所以能正常调用.

而通过GetProcAddress函数获取函数指针是传递的是字符串参数”add”, 这里相当于去Dll1.dll的name项中查找是否有名字完全一样的add 接口,这里已经发生名字改编,所以肯定是找不到的,所以调用失败.

通过序号动态加载

也可以通过序号获取函数指针,同样通过dumpbin工具可以得知Dll1.dll中导出函数的序号改编后的名称
动态链接库(三)--动态链接库的使用

上面提到,GetProcAddress函数的lpProcName参数指定的是导出函数的序号,那么该序号必须在低位字中,高位字必须是0.

这里可以借助MAKEINTRESOURCE宏,MAKEINTRESOURCE宏会把指定的函数序号转换为相应的函数名字字符串,即将int类型的序号转换成LPCSTR类型的变量:
动态链接库(三)--动态链接库的使用
动态链接库(三)--动态链接库的使用

FreeLibrary函数

使用完接口后记得使用FreeLibrary释放对Dll1.dll的引用,因为内存空间中只会加载一份Dll1.dll,供其他进程使用,当某一进程使用完后会释放对Dll1.dll的引用.

同其他内核对象一样,当操作系统捕获到Dll1.dll的引用次数为0时,即没有任何进程使用Dll1.dll时,就会卸载Dll1.dll,释放内存.

函数原型如下:

BOOL FreeLibrary(HMODULE hModule);

当不再需要访问动态加载的DLL时,使用该函数释放对DLL的引用.

if ( !FreeLibrary(hDll1) )
{
	//失败
}

与隐式加载DLL比较

对于同一类型的不同使用方式,这里免不了比较。目的不是去评价孰优孰劣,只是扩展了解下各自应用的场景,具体如何使用,取决于个人.

在动态加载DLL时,客户端程序不需要再包含导出函数声明的头文件(.h)和引入库文件(.lib),只需要.dll文件即可.

隐式链接实现比较简单,在编写客户端代码时就可以把链接工作做好,在程序中可以随时调用DLL导出的函数.

而动态显示加载的话,可以在需要的时候才加载DLL.

**应用场合:**在程序运行过程中只是在某个条件满足时才需要访问某个DLL中的某个函数时,可以考虑使用动态显示加载的方式访问DLL.

例:
假设某个程序需要访问十多个DLL,都采用隐式链接方式的话,那么在该程序启动时这些DLL都需要被加载到内存中,并映射到调用进程的地址空间,这将加大程序的启动时间.

而且一般来说,在程序运行过程中只是在某个条件满足时才需要访问某个DLL中的某个函数,在其他情况下都不需要访问这些DLL中的导出函数的话,将其加载到内存中资源浪费是比较严重的.

实际上, 采用隐式加载方式加载动态链接库时, 在程序启动时也是通过LoadLibrary函数加载该程序所需要的动态链接库的.

代码

最后附上本文涉及代码:文章来源地址https://www.toymoban.com/news/detail-439235.html

	//Dll1.cpp
	int DLL1_API add(int a, int b)
	{
		return a + b;
	}
	int DLL1_API subtract(int a, int b)
	{
		return a - b;
	}

	
	//main.cpp
	#include <iostream>
	using namespace std;
	
	//#pragma comment(lib, "D:\\vs2010_application\\动态库\\Dll1\\DLL_test\\lib\\Dll1.lib");
	//#pragma comment(lib, "lib/Dll1.lib");
	//
	//extern int _declspec(dllimport) add(int a, int b);
	//extern int _declspec(dllimport) subtract(int a, int b);
	
	
	//extern int _declspec(dllimport) add(int a, int b);
	//extern int _declspec(dllimport) subtract(int a, int b);
	
	#include <windows.h>
	#include <tchar.h>
	
	int main()
	{
		/*cout << "累加函数测试: " << add(5, 3) << endl;
		cout << "减法函数测试: " << subtract(5, 3) << endl;*/
	
		HMODULE hDll1 = LoadLibrary(_T("D:\\vs2010_application\\动态库\\Dll1\\Debug\\Dll1.dll"));
	
		if (hDll1 == NULL)
		{
			cout << "动态加载Dll1.dll失败!\n";
			return -1;
		}
	
		定义一个add函数指针类型PADDPROC
		//typedef int (*PADDPROC)(int a, int b);
		PADDPROC pAdd = (PADDPROC)GetProcAddress(hDll1, "add");
		//PADDPROC pAdd = (PADDPROC)GetProcAddress(hDll1, "?add@@YAHHH@Z");
		//if (pAdd == NULL)
		//{
		//	cout << "获取add函数指针失败!\n";
		//}
		//else
		//{
		//	cout << "成功获取add函数指针!\n";
		//}
	
		同理定义一个subtract函数指针类型PSUBPROC
		//typedef int (*PSUBPROC)(int a, int b);
		PSUBPROC pSub = (PSUBPROC)GetProcAddress(hDll1, "subtract");
		//PSUBPROC pSub = (PSUBPROC)GetProcAddress(hDll1, "?subtract@@YAHHH@Z");
		//if (pSub == NULL)
		//{
		//	cout << "获取subtract函数指针失败!\n";
		//}
		//else
		//{
		//	cout << "成功获取subtract函数指针!\n";
		//}
	
		//因为subtract函数返回类型以及参数列表均和add函数相同, 因此也可以用PADDPROC接收
		//PADDPROC pSub2 = (PADDPROC)GetProcAddress(hDll1, "subtract");
		//PADDPROC pSub2 = (PADDPROC)GetProcAddress(hDll1, "?subtract@@YAHHH@Z");
		//if (pSub2 == NULL)
		//{
		//	cout << "获取subtract函数指针2失败!\n";
		//}
		//else
		//{
		//	cout << "成功获取subtract函数指针2!\n";
		//}
		
	
		//通过序号获取函数指针
		typedef int (*PADDPROC)(int a, int b);
		PADDPROC pAdd = (PADDPROC)GetProcAddress(hDll1, MAKEINTRESOURCE(1));
		if (pAdd == NULL)
		{
			cout << "获取add函数指针失败!\n";
		}
		else
		{
			cout << "成功获取add函数指针!\n";
		}
	
	
		typedef int (*PSUBPROC)(int a, int b);
		PSUBPROC pSub = (PSUBPROC)GetProcAddress(hDll1, MAKEINTRESOURCE(2));
		if (pSub == NULL)
		{
			cout << "获取subtract函数指针失败!\n";
		}
		else
		{
			cout << "成功获取subtract函数指针!\n";
		}
	
		cout << "累加函数测试: " << pAdd(5, 3) << endl;
		cout << "减法函数测试: " << pSub(5, 3) << endl;
	
		if ( !FreeLibrary(hDll1) )
		{
			cout << "卸载Dll1.dll失败!\n";
		}
	
		getchar();		//system("pause");
		return 0;
	}

到了这里,关于动态链接库(三)--动态链接库的使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 动态链接库的__declspec(dllexport)关键字的概念

    在 Windows 操作系统下,创建一个动态链接库(DLL)项目时,您需要通过 __declspec(dllexport) 来显式地标记希望在 DLL 中 公开 的函数、类、变量等符号。这是因为在默认情况下,编译器会将函数和符号视为 私有 ,即它们不会在 DLL 导出表中注册,从而无法从外部访问(通常

    2024年02月11日
    浏览(36)
  • VS2022环境下C++ DLL动态链接库的编写和调用

    1、新建动态链接库项目 2、新建类 3、宏定义 4、使用宏定义修饰类 调用DLL动态链接库大致有两种方法: 一种是“隐式调用”,需要.h头文件、.lib符号文件、.dll动态库文件; 一种是“显示调用”,需要.dll动态库文件; 此处介绍第一种调用方式。 1、创建项目 2、项目属性

    2024年02月14日
    浏览(50)
  • Windows下C++静态链接库的生成以及使用

    这篇文章简单讨论一下Windows下如何使用VS生成和使用C++静态链接库,示例使用VS2022环境。 先创建C++项目-静态库 然后将默认生成的.h和.cpp文件清理干净,当然你也可以选择保留。 然后创建需要的.h和.cpp文件。 看下代码 很简单的代码,就是提供一个打印字符串的接口。编译一

    2024年02月05日
    浏览(38)
  • 【Linux】动静态库的使用与软链接的结合

    库本质就是把一堆(.o)后缀的文件也就是目标文件整合在一起 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库 在makefile中 1.第三方库的使用,gcc往后必须加上 -l +库名 2.如果系统中只提供静态链接,则gcc对其进行静态链

    2024年02月05日
    浏览(40)
  • 【linux深入剖析】深入理解软硬链接 | 动静态库的制作以及使用

    🍁你好,我是 RO-BERRY 📗 致力于C、C++、数据结构、TCP/IP、数据库等等一系列知识 🎄感谢你的陪伴与支持 ,故事既有了开头,就要画上一个完美的句号,让我们一起加油 软链接和硬链接是在Linux系统中常见的文件链接方式。 软链接(Symbolic Link): 软链接是一个指向目标文

    2024年04月13日
    浏览(46)
  • 【嵌入式学习笔记-02】什么是库文件,静态库的制作和使用,动态库的制作和使用,动态库的动态加载

    【嵌入式学习笔记-02】什么是库文件,静态库的制作和使用,动态库的制作和使用,动态库的动态加载 单一模型: 将程序中所有功能全部实现于一个单一的源文件内部。编译时间长,不易于维护和升级不易于协作开发。 分离模型 将程序中的不同功能模块划分到不同的源文件

    2024年01月15日
    浏览(56)
  • CMake TcpServer项目 链接静态库/动态库

    一、链接静态库   查看项目结构 CMakeLists.txt  执行命令和结果:   二、链接动态库 查看项目结构 CMakeLists.txt 执行命令和结果:

    2024年01月22日
    浏览(44)
  • 【opencv】示例-grabcut.cpp 使用OpenCV库的GrabCut算法进行图像分割

    left mouse button - set rectangle SHIFT+left mouse button - set GC_FGD pixels CTRL+left mouse button - set GC_BGD pixels 这段代码是一个 使用OpenCV库的GrabCut算法进行图像分割 的C++程序。它允许用户通过交互式方式选择图像中的一个区域,并利用GrabCut算法尝试将其分割出来。代码中包含用户操作指南、

    2024年04月13日
    浏览(45)
  • 基于boost库的搜索引擎项目

    boost库是指一些为C++标准库提供扩展的程序库总称,但是boost网站中并没有为我们提供站内搜索功能,因此我们要想找到某一个类的用法还要一个个去找,因此我们这次的目的就是实现一个搜索引擎功能,提高我们获取知识的效率 比如百度,谷歌,360等,这些都是大型的搜索

    2024年03月14日
    浏览(91)
  • 动态库、静态库的构建与使用

    所谓静态、动态是指 链接 。回顾一下,将一个程序编译成可执行程序的步骤: 链接 : 把外部函数的代码(通常是后缀名为.lib和.a的文件),添加到可执行文件中。这就叫做连接(linking)。这种通过拷贝,将外部函数库添加到可执行文件的方式,叫做静态连接(static linki

    2024年02月06日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包