首页 > 其他分享 >Dll基础

Dll基础

时间:2023-06-24 20:44:45浏览次数:34  
标签:lib int 基础 dll DLLTEST DynamicExport1 Dll 加载

DLL-基础

Windows 存在 3 个最重要的 dll, 分别如下

  1. kernel32.dll 用来管理内存,进程、线程
  2. user32.dll 用于处理用户界面相关的东西
  3. GDI32.dll 用来绘制和显示文字

使用 dll 有什么好处,可以参考官方说明

初步使用

创建动态的 dll 可以直接参考官方说明,或者 DynamicExport1, 如下

#pragma once

#ifdef DYNAMICLIB1_EXPORTS
#define LIBRARY_API_1 __declspec(dllexport)
#else
#define LIBRARY_API_1 __declspec(dllimport)
#endif

namespace DLLTEST
{
	class LIBRARY_API_1 DynamicExport1
	{
	public:
		int add(int l, int r);
		int sub(int l, int r);
	};
}

extern "C" LIBRARY_API_1 int Add(int l, int r);

//以下函数不会被导出
void InnerFun(void);

以下描述几个要点

符号的导入和导出(dllexport/dllimport)

msvc 通过 export 和 import 来区分导出项和导入项,从结果来看 export(可以通过 msvc 的 dumpbin /exports 来查看导出项)
我们注意到 dll 的生成结果通常包含以下几个(以 dynamic_lib_1 为例)

-a----         2023/6/17     22:40          10752 dynamic_lib_1.dll //包含完整符号信息和偏移量
-a----         2023/6/17     22:40           1582 dynamic_lib_1.exp //导出文件, 一般用于相互依赖的情况
-a----         2023/6/17     22:40           3212 dynamic_lib_1.lib //仅导出符号, 具体可见下文的查看符号
-a----         2023/6/17     22:40         937984 dynamic_lib_1.pdb //调试符号

关于 exp 文件可以参考这个
我们可以通过 dumpbin 查看 dll 和 lib 的符号, 如

:\Personal\learning-notes\05-platform\01-win\dll\x64\Release>dumpbin /exports dynamic_lib_1.lib
Microsoft (R) COFF/PE Dumper Version 14.35.32216.1
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file dynamic_lib_1.lib

File Type: LIBRARY

     Exports

       ordinal    name

                  ??4DynamicExport1@DLLTEST@@QEAAAEAV01@$$QEAV01@@Z (public: class DLLTEST::DynamicExport1 & __cdecl DLLTEST::DynamicExport1::operator=(class DLLTEST::DynamicExport1 &&))
                  ??4DynamicExport1@DLLTEST@@QEAAAEAV01@AEBV01@@Z (public: class DLLTEST::DynamicExport1 & __cdecl DLLTEST::DynamicExport1::operator=(class DLLTEST::DynamicExport1 const &))
                  ?add@DynamicExport1@DLLTEST@@QEAAHHH@Z (public: int __cdecl DLLTEST::DynamicExport1::add(int,int))
                  ?sub@DynamicExport1@DLLTEST@@QEAAHHH@Z (public: int __cdecl DLLTEST::DynamicExport1::sub(int,int))
                  Add

  Summary

          D5 .debug$S
          14 .idata$2
          14 .idata$3
           8 .idata$4
           8 .idata$5
          12 .idata$6

GCC 采用

/MT 和 /MD

MT 和 MD主要用来控制C/C++运行库是静态引用(MT)还是动态引用(MD),可以参考这个
MD 的发布时一般需要携带 MSVCR(versionnumber).DLL, 这也是我们经常见到 msvc110~170 的原因.

extern "C"

extern "C" 用于处理名称修饰的问题(去掉namespace), 具体用法可以参考这个

ABI 的兼容性

Visual Studio 2013 及更早版本中的 Microsoft C++ (MSVC) 编译器工具集不保证主版本间的二进制兼容性。 无法链接由这些工具集的不同版本生成的对象文件、静态库、动态库和可执行文件。 ABI、对象格式和运行时库不兼容。我们在 Visual Studio 2015 及更高版本中改变了此行为。 由其中任一版本的编译器编译的运行时库和应用具有二进制兼容性。 这反映在 C++ 工具集主版本号中,对于自 Visual Studio 2015 以来的所有版本,该版本号都以 14 开头。 (对于 Visual Studio 2015、2017、2019 和 2022,工具集版本分别为 v140、v141、v142 和 v143)。 假设你具有 Visual Studio 2015 生成的第三方库。 你仍可在 Visual Studio 2017、2019 或 2022 生成的应用程序中使用它们。 无需使用匹配工具集重新编译。 最新版本的 Microsoft Visual C++ 可再发行程序包(可再发行程序包)适用于所有版本。
具体可以参考这个

静态载入、延迟载入、动态载入

我们更经常使用静态载入的方式,动态载入的方式更多使用在插件处理上, 而延迟加载更多用在有性能问题的时候

动态载入

动态载入我们经常会在插件化的情况使用, 以下是一般的使用过程

//1. 创建C++类接口和实现
//提供 virutal 接口,实现多态
namespace DLLTEST
{
	class DynamicExport2Base
	{
	public:
		virtual ~DynamicExport2Base() = 0; // 析构函数的需要具体实现
		virtual int add(int left, int right) = 0;
		virtual int sub(int l, int r) = 0;
	};


	class DynamicExport2Impl : public DynamicExport2Base
	{
	public:
		DynamicExport2Impl();
	    virtual ~DynamicExport2Impl();
		virtual int add(int l, int r) override;
		virtual int sub(int l, int r) override;
	};
}
//2.导出 C 风格,用于动态加载
extern "C"
{
	LIBRARY_API_2 DLLTEST::DynamicExport2Base *createObject(void);
	LIBRARY_API_2 void releaseObject(const DLLTEST::DynamicExport2Base*);	
}

//3. 加载模块
HMODULE dynamicModule2 = ::LoadLibraryEx(L"dynamic_lib_2", 0, NULL);
if (dynamicModule2 == nullptr)
{
	cout << "loadlibrary fail:" << ::GetLastError() << "\n";
	return 0;
}

//4. 获取调用对象的函数指针
typedef void* (*pFunCreateObject)(void);
typedef void (*pFunReleaseObject)(void*);

pFunCreateObject funCreateObj = (pFunCreateObject)::GetProcAddress(dynamicModule2, "createObject");
if (funCreateObj == nullptr)
{
	cout << "get create fun fail:" << ::GetLastError() << "\n";
	::FreeLibrary(dynamicModule2);
	return 0;
}

pFunReleaseObject funReleaseObj = (pFunReleaseObject)::GetProcAddress(dynamicModule2, "releaseObject");
if (funReleaseObj == nullptr)
{
	cout << "get release fun fail:" << ::GetLastError() << "\n";
	::FreeLibrary(dynamicModule2);
	return 0;
}

//5. 创建相应的对象
DynamicExport2Base* dynamicObj = (DynamicExport2Base*)funCreateObj();
if (dynamicObj == nullptr)
{
	cout << "create dynamic object 2 fail:" << ::GetLastError() << "\n";
	::FreeLibrary(dynamicModule2);
	return 0;
}

//6. 根据偏移量调用接口
cout << "add(1,100)=" << dynamicObj->add(1, 100) << "\n";
	
//7. 释放对象
funReleaseObj(dynamicObj);

//8. 释放动态加载
::FreeLibrary(dynamicModule2);
return 0;

延迟加载

延迟加载有一些限制,具体可以参考这边
那什么时候会用到这个?

  1. 模块要加载的 dll 数量多, 很影响启动速度
  2. 有一些版本上的接口需要处理(当然这个通常直接采用 version 的方式处理, 在此处需要通过捕获异常处理)

使用

使用延迟加载有几个事项

  1. 项目->链接器->输入->延迟加载 dll 添加需要加载的 dll 完整信息即 cl 选项的 /DELAYLOAD:"delay_lib.dll"
  2. 其他的使用同照常的静态链接使用即可;
    具体可以参考 app/delayload.cpp 的实现

卸载

若需要动态的卸载延迟加载的模块,可以以下步骤实现

  1. 项目->链接器->高级->卸载延迟加载的 dll选择/DELAY:UNLOAD选项
  2. 在代码中动态的调用 __FUnloadDelayLoadedDLL2 实现卸载
    具体可以参考 app/delayload.cpp 的实现

DllMain

可以参考这个
TODO: 在了解 TLS 来完善这个部分

参考

标签:lib,int,基础,dll,DLLTEST,DynamicExport1,Dll,加载
From: https://www.cnblogs.com/hejianglin/p/17501654.html

相关文章

  • 容器基础-- namespace,Cgoup 和 UnionFS
    Namespace什么是Namespace?这里的"namespace"指的是Linuxnamespace技术,它是Linux内核实现的一种隔离方案。简而言之,Linux操作系统能够为不同的进程分配不同的namespace,每个namespace都具有独立的资源分配,从而实现了进程间的隔离。如果你的Linux安装了GCC,可以通过......
  • [matplotlib] 基础知识
    基本架构脚本层(scripting)脚本层是Matplotlib结构中的最顶层。我们编写的绘图代码大部分代码都在该层运行,它的主要工作是负责生成图形与坐标系。美工层(artist)美工层是结构中的第二层,它提供了绘制图形的元素时的给各种功能,例如,绘制标题、轴标签、坐标刻度等。后端......
  • 区块链基础之密码学及安全技术
    1.2密码学及安全技术1.2.1密码学知识1.2.1.1Hash函数Hash(哈希)哈希函数是一类数学函数,可以在有限合理的时间内,将任意长度的消息压缩为固定长度的输出值,并且是不可逆的。其输出值称为哈希值,也称为散列值。哈希算法的应用:消息认证:确保收到的消息和发送的消息都是未......
  • go编程基础--类型与变量
    引用:第3课:类型与变量?|课程列表|《Go编程基础(视频)》|Go技术论坛(learnku.com)1.go编排组织结构: 2.类型: rune表明用于unicode字符操作 3.默认值:值类型与引用类型 引用类型的默认值是nil ......
  • 区块链基础之密码学及安全技术
    1.2密码学及安全技术1.2.1密码学知识1.2.1.1Hash函数Hash(哈希)哈希函数是一类数学函数,可以在有限合理的时间内,将任意长度的消息压缩为固定长度的输出值,并且是不可逆的。其输出值称为哈希值,也称为散列值。哈希算法的应用:消息认证:确保收到的消息和发送的消息都是未......
  • 【linux命令】“最强大的编辑器”vim用法简介(基础篇)
    vim编辑器是所有Unix及Linux系统下标准的编辑器,它的强大不逊色于任何最新的文本编辑器。它主要分为命令令行模式、插入模式和底行模式这三种,下面主要介绍一下这三种模式最简单常用的用法。一.命令行模式1.移动光标左移:h光标右移:l光标上移:k光标下移:j光标向右移动一个单词:w(联......
  • 【python基础】文件-初识文件
    文本文件可存储的数据量是非常多的。每当需要分析或修改存储在文件中的信息时,首先就是读取文件到内存中,为此可以一次性读取文件的全部内容,也可以以每次一行的方式逐步读取。1.读取文件1.1读取整个文件要读取文件,需要一个包含几行文本的文件。下面首先来创建一个poems文本文件,,里......
  • 【人工智能技术专题】「入门到精通系列教程」零基础带你进军人工智能领域的全流程技术
    前言人工智能是一个庞大的研究领域。虽然我们已经在人工智能的理论研究和算法开发方面取得了一定的进展,但是我们目前掌握的能力仍然非常有限。机器学习是人工智能的一个重要领域,它研究计算机如何模拟或实现人类的学习行为,以获取新的知识或技能,并通过重新组织已有的知识结构来不断提......
  • Spark基础
    Spark是一种基于内存的快捷、通用、可扩展的大数据分析引擎1.Spark模块SparkCore:Spark核心模块,包含RDD、任务调度、内存管理、错误恢复、与存储系统交互等SparkSQL:用于处理结构化数据的一个模块,提供了2个编程抽象:DataFrameDataSet,并且作为分布式SQL查询引擎的作用。他将H......
  • Linux 基础网络设置
    Linux基础网络设置拓扑图:推荐步骤:➢根据拓扑给DHCP服务➢配置修改实验步骤:根据拓扑给修改重新启动网卡服务查看查看挂载系统光盘查看挂载的系统光盘信息安装二、配置修改查看保留修改启动查看修改网卡配置文件配置保留自动获取保留设置配置自动获取释放和更新租约查看获取查看......