DLL的两种加载方式——静态加载和动态加载

本文对比分析了静态加载和动态加载DLL库的优缺点,详细介绍了两种加载方式的具体步骤,帮助开发者理解并选择适合项目的DLL加载策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

  • 案例简述

在某项目中,需要使用两个不同版本的HCNetSDK库,我们通常使用的静态加载DLL的方式不能满足该需求,故用到动态加载DLL的方式。

  • 背景技术及术语解释

静态加载:也称隐式调用,指在运行程序之前由操作系统的加载器将DLL和EXE一起加载到内存里。注意这里与程序的静态链接区别开来,静态链接发生在编译过程之中,而DLL的静态加载是发生在程序运行之前。

动态加载:也称显示调用,指应用程序运行过程中程序自己完成对DLL的加载和卸载(DLL加载—DLL函数地址获取—DLL释放),应用程序在执行过程中随时可以加载DLL文件,也可以随时卸载DLL文件。

  • 详细描述
  1. 静态加载DLL

静态加载DLL需要提供.h、.lib、.dll文件,假设需要加载的DLL文件名为MyDll.dll,静态加载DLL步骤如下:

(1)将MyDll.dll拷到目标工程(需调用MyDll.dll的工程)的Debug目录下;

(2)将MyDll.lib拷到目标工程(需调用MyDll.dll的工程)目录下;

(3)将MyDll.h(包含输出函数的定义)拷到目标工程(需调用MyDll.dll的工程)目录下;

(4)打开目标工程,选中目标工程名点击右键,依次选择【属性】-【配置属性】-【链接器】-【输入】,在附加依赖项中输入:MyDll.lib;

(5)在目标工程Head Files加入MyDll.h文件;

(6)在目标工程(*.cpp,需要调用MyDll.dll中的接口)中包含头文件,即#include “MyDll.h”,然后在该*.cpp中可直接调用MyDll.h中的接口。

  1. 动态加载DLL

动态加载DLL不需要依赖.h和.lib文件,仅需要将.dll文件拷贝到应用程序所在目录下。

动态加载需主要用到LoadLibrary(加载DLL)、GetProcAddress(获得DLL中API函数的地址)、FreeLibrary(释放DLL)这几个系统函数。

LoadLibrary() 函数作用是将指定的可执行模块映射到调用进程的地址空间,如果调用成功, LoadLibrary() 函数将返回所加载的那个模块的句柄。当获取到动态链接库模块的句柄后,接下来就要想办法获取DLL导出函数的地址,这可以通过调用 GetProcAddress() 函数来实现。当DLL文件中的函数不再使用或程序结束时,需要使用FreeLibrary()函数对其进行释放。具体使用示例如下:

加载DLL:

#include <windows.h>

HINSTANCE hDllInst = LoadLibrary(“MyDll.dll”);

 

调用DLL中函数:

typedef  BOOL (CALLBACK * pwmNET_DVR_Logout_V30)(long lUserID);

BOOL CLASSNAME::NET_DVR_Logout_V30(long lUserID)

{

 pwmNET_DVR_Logout_V30 pNET_DVR_Logout_V30 = (pwmNET_DVR_Logout_V30)GetProcAddress(hDllInst, " NET_DVR_Logout_V30");

 if (NULL == pNET_DVR_Logout_V30)

 {

       PAG_ERROR(">>> GetProcAddress [NET_DVR_Logout_V30] Failed!");

       return FALSE;

}

return pNET_DVR_Logout_V30(lUserID);

}

 

释放DLL:

if(hDllInst!= NULL)

{

   FreeLibrary(hDllInst);

}

  1. 静态加载与动态加载区别

(1)加载时间:静态加载发生在程序运行之前,故如果缺少dll文件,程序运行不起来。动态加载发生在程序运行过程中(由编程者决定何时加载),不会因为缺少dll,导致整个程序运行不起来。如果程序体积较大,功能较为复杂,静态加载会导致程序启动时间长。而动态加载可将较大的程序分开加载的,程序运行时只需要将主程序载入内存,程序启动快。

(2)依赖文件:静态加载需要.lib和.dll文件,动态加载只需要.dll文件。

(3)占用内存: 静态加载占用内存较大。静态加载可以在需要的时候用LoadLibrary进行加载,在不需要的时候用FreeLibrary进行卸载,这样可以不必占用内存。

(4)使用情况:静态加载使用简单方便。动态加载使用复杂一些,需要显示获取函数地址。

  • 贡献点

(1)介绍了DLL库静态加载和动态加载的方法及其区别。

(2) 加深了对DLL库静态加载和动态加载的理解,便于以后项目开发中选择合适的加载方式。

程序中加载了一个DLL文件,但生成的EXE在脱离了DLL文件后仍然可以 单独使用,这是动态加载DLL技术。即:调用资源中的DLL。 此技术的好处:EXE可以使用DLL中的函数,但不会额外增加一 个DLL文件,在使用DLL文件的时候不需要先把DLL释放到硬盘。 在动态加载的这个DLL中定义了一个函数MRun,该函数可以动态执行一 个EXE,即:调用资源中的EXE文件或TMemoryStream中被载入的EXE流。 此技术的好处:直接把资源中的EXE加载到内存中执行,使用程序自 身嵌入的EXE文件的时候不需要先把EXE释放到硬盘上就可以直接执行。 对保密EXE文件很有用。例如:我编写的程序是A.exe,它在运行后需要 使用B.exe,而B.exe是别人编写的我没有源码,但我必须又要在我的程 序中用B.exe,这时我就把它包含到我的A.exe中,这个非常容易做到, 但是,程序A.exe在使用程序B.exe的时候按照常理必须先把B.exe释放 到硬盘上才可以用WinExec或ShellExecute等函数调用它,但你在释放 到硬盘上的时候容易被别人直接复制走,而你只想让别人用你的A.exe不 想让别人直接用B.exe(因为B.exe是别人写的等原因),此时如何保密 B.exe呢?这时只要用到上面所说的MRun函数就可以了,程序A.exe在执 行B.exe的时候不需要释放到硬盘上就可以直接执行B.exe啦,是不是很爽? 说一下MRun的调用方式: MRun(流,参数,进程id); 调用成功返回True,失败返回False,三个参数解释如下: 第一个参数:一个载入了EXE的资源流或者内存流等流类型。 第二个参数:传递调用EXE的参数。如果EXE调用不需要参数,可设置为空串。 第三个参数:如果调用成功,则返回被调用的EXE对应的进程ID。 细节性问题,请直接双击Project1.dpr文件进入演示代码,了解更多。演示代 码中动态加载了MemRun.dll文件,动态调用了5555044.exe文件,如果你想更换 动态调用的EXE文件,只需要用其它EXE覆盖5555044.exe文件并双击Clear.bat 文件后,在Delphi中按F9重新编译运行即可。
### Qt 动态加载方式 对于Qt应用程序而言,存在两种主要的方式用于加载动态库:静态加载动态加载。 #### 静态加载 当提到静态加载动态库的概念时,实际上是指在编译阶段就指定好依赖的共享库,在程序启动初期由操作系统负责解析并加载这些库。这种方式下,虽然术语中包含了“静态”,但实际上涉及的是共享对象(Linux下的`.so`文件或Windows下的`.dll`文件)。这种加载模式不需要程序员额外编写代码来处理库的具体加载逻辑;只需要确保链接器能够找到所需的库即可[^1]。 然而需要注意的是,“静态加载”的说法并不常见于描述动态库的行为,通常所说的静态加载指的是将静态库直接嵌入到最终可执行文件之中,如通过命令 `ar rcs libstatic_library.a static_library.o` 创建静态库,并将其链接至目标应用[^3]。 #### 动态加载 相比之下,真正的动态加载则是指在运行期间按需加载特定功能模块的技术。这允许开发者仅在必要时刻才引入外部资源,从而减少初始内存占用以及提高灵活性。为了实现这一点,可以利用Qt提供的API函数如`QLibrary::load()` `resolve()`, 或者标准C++中的`dlopen()/dlsym()` (POSIX) 及其对应物`LoadLibrary()/GetProcAddress()`(Win32 API)[^2]。 具体来说: - **使用 QLibrary 类** 下面是一个简单的例子展示如何使用Qt框架内的`QLibrary`类来进行动态加载操作: ```cpp #include <QApplication> #include <QLibrary> int main(int argc, char *argv[]) { QApplication app(argc, argv); QString libraryPath = "./path/to/your/library"; // 库的位置 QLibrary myLib(libraryPath); if (!myLib.load()) { /* 错误处理 */ } typedef void (*FunctionPtr)(); FunctionPtr func = reinterpret_cast<FunctionPtr>(myLib.resolve("functionName")); if(func){ func(); // 调用函数 }else{ qDebug()<<"Failed to resolve function"; } return app.exec(); } ``` 综上所述,Qt支持多种方法来加载动态库,既可以是在构建过程中预先定义好的形式——即所谓的“静态加载”(尽管严格意义上并非如此),也可以是完全基于需求驱动的真正意义上的动态加载过程。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值