动态链接库dll详解

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

动态链接库概述

    DLL就是整个Windows操作系统的基础。动态链接库不能直接运行,也不能接收消息他们是一些独立的文件。

    Windows API中的所有函数都包含在DLL中。

    其中有三个最重要的DLL

    kernel32.dll,它包含用于管理内存、线程和进程的各个函数;

    User32.dll,它包含用于执行用户界面任务(如窗口的创建和消息的传送)的各个函数

    GDI32.dll,它包含用于画图和显示文本的各个函数

静态库和动态库

    静态库:函数和数据被编译进一个二进制文件(通常扩展名为.LID)。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其他模块组合起来创建最终的可执行文件(.exe文件);

    在使用动态库的时候,往往提供两个文件:一个因入库和一个DLL。因入库包含被DLL导出的函数和变量的符合名,DLL包含实际的函数和数据。在编译链接可执行文件时,只需要链接引入库,DLL中的函数代码和数据并不复制到可执行文件中,在运行时候,再去加载DLL,访问DLL中导出的函数。

DLL的创建和使用

动态链接库的创建

新建一个动态链接库

动态链接库dll详解

新建一个.cpp文件,在头部添加 #include"pch.h"预编译文件。 应用程序想要访问动态库函数需要先将该函数导出,使用_declspec(dllexports)关键字可以将该函数导出。

#include"pch.h"

_declspec(dllexport) int add(int a, int b) {
	return (a + b);
}


_declspec(dllexport) int substract(int a, int b) {
	return (a - b);
}

通过dumpbin 指令可以查看我们编写的dll有哪些导出函数

动态链接库dll详解

去到新建的.dll目录下。

动态链接库dll详解

输入

dumpbin -exports lib文件名即可查看该dll文件有哪些函数被导出。

动态链接库dll详解

 ordinal表示导出函数的序号

hint表示提示码,意义不大

RVA表示导出函数在dll模块中的位置

name表示导出函数名,编译器会根据自己的规则更改函数名,以保证函数重载。

动态链接库的使用

新建一个基于对话框的MFC应用

动态链接库dll详解

添加两个按钮,点击add按钮会调用dll文件中的add函数,点击“substract"按钮会调用dll文件中的substract函数

这里介绍隐式加载,隐式加载需要lib文件

使用动态库前要先将.dll文件加入到程序文件目录下

第一种方法:

在程序里添加#pragma comment(lib,"1124TDll.lib")代码

第二种方法:

将lib文件添加到附加依赖项中。

动态链接库dll详解

两种方法选一种即可 

在需要用到的动态库函数用关键字extern声明

extern int add(int a, int b);
extern int substract(int a, int b);
void CDllMFCTestDlg::OnBnClickedAddBtn() {
	CString str;
	str.Format(L"4+3 = %d", add(4, 3));
	MessageBox(str);
}

void CDllMFCTestDlg::OnBnClickedSubstractBtn() {
	CString str;
	str.Format(L"4-3 = %d", substract(4, 3));
	MessageBox(str);
}

动态链接库dll详解

我们也可以通过dumpbin命令查看生成的应用程序有哪些导入的dll 

在开发者提示命令里输入 dumpbin -imports 项目名,即可查看.exe添加的所有lib

动态链接库dll详解

DLL的导入和导出以及条件编译 

导入和导出

dll的导入和导出需要用到两个关键字_declspec(dllimport)和_declspec(dllexport)

_declspec(dllexport)是在类、函数以及数据声明时候使用。把dll里面的相关代码暴露出来给其他应用程序使用。提供给别的应用程序使用,标识提供者。共dll内部使用。

例如在编写dll库时:

_declspec(dllexport)int add(int a,int b) {
    return a+b;
}

_declspec(dllimport)是在外部程序需要使用DLL内相关内哦让那个使用的标识符。是把dll中的相关代码插入到应用程序中去。表示使用者。不是dll内部使用。

例如某个项目使用dll库中的add函数,先使用_declspec(import)关键字声明。

_declspec(dllimport)int add(int a,int b);

条件编译指令

使用条件编译的目的是使用“dll+头文件”的方式使用动态链接库,这样可以头文件中查看动态库中的函数声明,而不需管函数的具体实现。

在DLL.cpp中

添加 

#define DLL1_API _declspec(dllexport)

Dll1.cpp 

#include"pch.h"
#include"Dll1.h"

#define DLL1_API _declspec(dllexport)
int add(int a, int b) {
	return (a + b);
}


int substract(int a, int b) {
	return (a - b);
}	

在DLL.h中声明函数

#ifdef DLL1_API
#else
#define DLL1_API _declspec(dllimport) 
#endif
DLL1_API int add(int a, int b);
DLL1_API int substract(int a, int b);

在编译dll文件时,DLL1_API已经在.cpp文件中被定义过而不会在.h中再被定义,因此它的值为_declspec(dllexport),也就是将函数导出。

而当某个项目需要用到dll库时,只需要在程序中田间#include"dll.h的路径”,这样在该项目只会编译dll.h文件,DLL1_API又会被定义为_declspec(dllimport),这样这些函数相对于项目来说又编程导入函数。

从DLL中导出C++类

如果在声明类的时,指定了导出标志,则该类中所有函数都被导出;

#ifdef DLL1_API
#else
#define DLL1_API _declspec(dllimport) 
#endif


class  DLL1_API Point {
public:
	void outPut(int x, int y);
	void test();
};

动态链接库dll详解

 如果只给某个成员函数指定导出标志,则只导出指定标志的函数。

#ifdef DLL1_API
#else
#define DLL1_API _declspec(dllimport) 
#endif


class   Point {
public:
	void DLL1_API outPut(int x, int y);
	void test();
};

动态链接库dll详解

解决名字改编的问题 

假人我们使用C++写了一个dll,而使用C语言去调用该函数。因为编译器已经将函数名字改编(名字粉碎),因此在调用时会报出”找不到标识符“的错误(除非调用时使用的是粉碎后的函数名)。

因此我们希望dll在编译时,导出函数的名称不要改编

导出时添加extern "C"

#define DLL1_API extern "C" _desclpec(dllimport)

Dll.h文件 

#ifdef DLL1_API
#else
#define DLL1_API extern "C" _declspec(dllimport) 
#endif

DLL1_API int  add(int a, int b);
DLL1_API int  substract(int a, int b);

Dll.cpp文件 

#define DLL1_API extern "C" _declspec(dllexport)
#include"Dll1.h"

int add(int a, int b) {
	return (a + b);
}

int substract(int a, int b) {
	return (a - b);
}

编译器就不会进行名字改编,一个用C语言编写的客户端程序可以调用C++的Dll。其缺点是不能导出类的成员函数,只能用于导出全局函数这种情况。 

动态链接库dll详解

使用_stdcall 标准的调用约定


  Dll.h文件 

#ifdef DLL1_API
#else
#define DLL1_API extern "C" _declspec(dllimport) 
#endif

DLL1_API int  _stdcall add(int a, int b);
DLL1_API int  _stdcall substract(int a, int b);

Dll.cpp文件 

#define DLL1_API extern "C" _declspec(dllexport)
#include"Dll1.h"

int _stdcall add(int a, int b) {
	return (a + b);
}

int -stdcall substract(int a, int b) {
	return (a - b);
}

 导出效果一样嘎嘎棒

动态链接库dll详解

如果导出函数采用了是标准调用约定_stdcall,访问这个dll的调用方也要采用该约定类型来访问响应的导出函数

使用def模块文件

新建一个.def文件

动态链接库dll详解

编写代码 

动态链接库dll详解

EXPORTS即调用_stdcall约定,也不会发送改编,只会调用这里显示的 

EXPORTS语句引入一个由多个definitions组成的节。每个定义必须在单独一行。EXPORTS关键字需要放在第一个定义所在的同一行或前一行 。.def文件可以包含一个或多个EXPORTS语句

导出的definitions的语法为: 

entryname[=internalname][@ordinal][NONAME][PRIVATE][DATA]
  • entryname是导出的函数名,这是必选的。如果导出的名称与DLL中的名称不同,则通过internalname指定DLL中导出的名称。例如,如果DLL导出的函数fun1(),要将它用作fun2(),则应指定
  • @ordinal允许指定是序号而不是函数名将进入DLL的导出表,这有助于最小化DLL的大小。LIB文件将包含序号与函数之间的映射,这使得可以像在使用DLL的项目中那样使用函数名。
  • 可选的NONAME关键字允许只按序号导出,并减小DLL中到处白哦的大小。但是,如果要在DLL上是使用GetProcAddress,则必须直到序号,因为名称将无效 

Dll动态加载 

使用LoadLibrary函数 加载动态库

HINSTANCE hInst = LoadLibraryA("1124TDll.dll");

函数执行成功,则返回该模块的句柄;函数执行失败,返回NULL。 

定义一个函数指针,并从dll中获取函数地址

因为dll中导出函数采用的是标准调用约定,访问这个dll的调用方也要采用该约定类型来访问响应的导出函数。

typedef int(_stdcall *ADDPROC)(int a, int b);//定义一个函数指针
ADDPROC Add = (ADDPROC)GetProcAddress(hInst, "add");//从dll中获取函数地址

GetProcAddress函数执行成功返回函数地址,失败返回NULL

if (!Add) {
	MessageBox(L"获取函数地址失败");
	TRACE("GetProcAddress errorcode :%d", GetLastError());
	return;
}

我们就可以通过这个函数指针使用这个函数了

CString str;
str.Format(L"4+3 = %d", Add(4, 3));
MessageBox(str);

用完之后通过FreeLibray函数释放

FreeLibrary(hInst);

GetProcAddress函数不仅可以通过函数名称导出,也可以通过序号导出。

如果导出函数名称例如下面这种已经被名称粉碎了,那么也必须使用这种名称

动态链接库dll详解 也必须跟着用这个名称

typedef int(_stdcall *ADDPROC)(int a, int b);
ADDPROC Add = (ADDPROC)GetProcAddress(hInst, "?Add@@YAHHH@Z");

或者通过序号获取导出函数

ADDPROC Add = (ADDPROC)GetProcAddress(hInst, MAKEINTRESOURCEA(1));

动态链接库dll详解

DllMain函数

对可执行模块而言,入口时winmain;对dll而言,入口时Dllmain。但对dll来说,Dllmain是可选的,如果提供了Dllmain,系统加载时会调用Dllmain。

当我们在编写Dll时,如果某些函数需要用到当前Dll模块句柄,那么就可以为该Dll提供dllmain函数,然后头通过参数hMoudule,保存在一个全局变量里面,以供其他函数使用。

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:   //进程第一次加载Dll,并调用Dllmain函数
    case DLL_THREAD_ATTACH:    //当前进程正在创建一个线程
    case DLL_THREAD_DETACH:    //线程结束
    case DLL_PROCESS_DETACH:   //进程结束
        break;
    }
    return TRUE;
}

Dll有三个参数,

参数hMoudle表示动态链接库的模块句柄,当dll初次被加载时,句柄可以通过这个参数传递过来

参数ur_reason_for_call表示加载原因

参数lpReserved用处不大,保留参数。为NULL表示动态加载,非NULL表示静态加载

动态链接库dll详解

 文章来源地址https://www.toymoban.com/news/detail-459117.html

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

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

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

相关文章

  • [python] 罗技动态链接驱动库DLL 控制 键鼠

    最近在玩搬砖游戏晶核, 每天有很多重复繁琐的\\\"打卡\\\"操作, 得知隔壁御三家游戏就有大佬做了自动收割的辅助工具,我就想模仿写一个. 不过大佬们写的开源工具厉害得多,加了神经网络自动识别,实现寻路和点击功能.我目前最多就是实现一个简单连点器加色块识别而已,而且这种

    2024年02月19日
    浏览(81)
  • Rust:使用libloader调用动态链接库 (DLL)

    掘金为同人创作:掘金 最近需要使用Rust 动态调用 动态链接库,本来打算是使用 libloading 的,但是 libloading 在调用dll中的函数的时,是必须要在编译时确定参数和return的类型的。但后来发现了 libloader 这个包包, libloader 是基于 libloading 的,但是操作起来却比 libloader 方便。

    2023年04月10日
    浏览(50)
  • 以动态库链接库 .dll 探索结构体参数

    Dev c++ C语言实现第一个 dll 动态链接库 创建与调用-CSDN博客  在写dll 插件中发现的函数指针用途和 typedef 的定义指针的用法-CSDN博客 两步之后,尝试加入结构体实现整体数据使用。 注意结构体 Ak 是相同的 代码如下 DLL文件有两个,dll.dll是上面提到的链接里的 dllv2.dll是这个代

    2024年04月12日
    浏览(38)
  • 前端(node.js)调用dll动态链接库

    使用 js node 调用dll 动态链接库. github地址如下,包含dll,里面就一个Add方法暴露出来 github Windows 11 22H2 node v16.20.0 Python 3.11.2 需要安装这俩库 ffi-napi app.js 第一种不好使,曲线救国的方式这个是。 python-shell app.js python

    2024年02月16日
    浏览(46)
  • VS2019编译生成动态链接库dll的两种方式

     dll项目的默认结构如下:  四个文件的内容因为是默认生成的,不是特别重要, 接下来就是重要的修改部分: 方法一: 修改“pch.h”和“dllmain.cpp”文件,可以参考以下博主链接,但博主的引用部分有些繁琐,文末会介绍我的引用方法,和正常引用外部库步骤是一样的。这

    2023年04月09日
    浏览(48)
  • Unity——在C#中调用C++动态链接库(DLL)

    1、新建C++空项目 打开VS,新建一个C++空项目,自命名项目名称与位置。 2、配置项目属性为动态链接库 右键项目,点击属性,打开项目属性页,将常规中的配置类型改为动态库(.dll)。  3、添加.h头文件 右键头文件,点击添加—新建项,选择头文件.h,命名为DllForUnity.h,点击

    2024年02月10日
    浏览(46)
  • Python 使用 ctypes 调用 C/C++ DLL 动态链接库

    ctypes 有以下优点: Python内建,不需要单独安装 Python可以直接调用C/C++ 动态链接库(.dll 或 .so) 在Python一侧,不需要了解 c/c++ dll 内部的工作方式 提供了 C/C++ 数据类型与Python类型的相互映射,以及转换,包括指针类型。 ctypes 在下列场景可以发挥较大作用 运算量大的操作可以写

    2024年02月06日
    浏览(50)
  • VS2019 DLL动态链接库生成多个正在运行的Windows应用之间共享的DLL [三]

              本例程演示如何使用 Visual Studio IDE 通过 Microsoft C++ (MSVC) 编写自己的动态链接库 (DLL)。 然后,该演练演示如何从其他 C++ 应用中使用 DLL。 DLL(在基于 UNIX 的操作系统中也称为“共享库”)是最有用的 Windows 组件类型之一 。 可以将其用作共享代码和资源、缩小应

    2024年02月17日
    浏览(41)
  • [速成] Visual Studio C/C++创建Dll(动态链接库)并调用

    以下示例均在VS2022环境下完成。 注意: _EXPORTING 是笔者 自定义宏 ,用于区分当前是导出dll还是调用dll,要实现导出函数,还需要在实现Dll函数功能的 项目属性 里,添加 预处理定义 (记得结尾加 分号 ),如下图: _declspec(dllexport) 是VC的,表示 导出函数 到dll; _declspec(dllim

    2024年02月04日
    浏览(47)
  • windows VS2015 Steup 打包发布软件并附带动态链接库dll

    打包:  方法一:VS 2015 Steup 打包发布软件_柠檬野生菌的博客-CSDN博客_vs2015 exe发布  1、插件下载 Microsoft Visual Studio 2015 Installer Projects 工具-扩展与更新-联机-搜索 Installer Projects 2、安装 关掉VS 双击下载好的 .exe 安装 3、打开VS新建Steup Project项目 注意项目名称 需要跟你打包的

    2024年02月06日
    浏览(51)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包