动态链接库DLL的加载:隐式加载(载入时加载)和显式加载(运行时加载)

静态链接库在链接时,编译器会将 .obj 文件和 .LIB 文件组织成一个 .exe 文件,程序运行时,将全部数据加载到内存。

如果程序体积较大,功能较为复杂,那么加载到内存中的时间就会比较长,最直接的一个例子就是双击打开一个软件,要很久才能看到界面。这是静态链接库的一个弊端。

动态链接库有两种加载方式:隐式加载和显示加载。

  • 隐式加载又叫载入时加载,指在主程序载入内存时搜索DLL,并将DLL载入内存。隐式加载也会有静态链接库的问题,如果程序稍大,加载时间就会过长,用户不能接受。
  • 显式加载又叫运行时加载,指主程序在运行过程中需要DLL中的函数时再加载。显式加载是将较大的程序分开加载的,程序运行时只需要将主程序载入内存,软件打开速度快,用户体验好。

隐式加载

首先创建一个工程,命名为 cDemo,添加源文件 main.c,内容如下:

  1. #include<stdio.h>
  2. extern int add(int, int); // 也可以是 _declspec(dllimport) int add(int, int);
  3. extern int sub(int, int); // 也可以是 _declspec(dllimport) int sub(int, int);
  4. int main(){
  5. int a=10, b=5;
  6. printf("a+b=%d\n", add(a, b));
  7. printf("a-b=%d\n", sub(a, b));
  8. return 0;
  9. }

找到上节创建的 dllDemo 工程,将 debug 目录下的 dllDemo.lib 和 dllDemo.dll 复制到当前工程目录下。

前面已经说过:.lib 文件包含DLL导出的函数和变量的符号名,只是用来为链接程序提供必要的信息,以便在链接时找到函数或变量的入口地址;.dll 文件才包含实际的函数和数据。所以首先需要将
dllDemo.lib 引入到当前项目。

选择”工程(Project) -> 设置(Settings)“菜单,打开工程设置对话框,选择”链接(link)“选项卡,在”对象/库模块(Object/library modules)“编辑框中输入
dllDemo.lib,如下图所示:

但是这样引入 .lib 文件有一个缺点,就是将源码提供给其他用户编译时,也必须手动引入 .lib 文件,麻烦而且容易出错,所以最好是在源码中引入 .lib 文件,如下所示:

#pragma comment(lib, "dllDemo.lib")

更改上面的代码:

  1. #include<stdio.h>
  2. #pragma comment(lib, "dllDemo.lib")
  3. _declspec(dllimport) int add(int, int);
  4. _declspec(dllimport) int sub(int, int);
  5. int main(){
  6. int a=10, b=5;
  7. printf("a+b=%d\n", add(a, b));
  8. printf("a-b=%d\n", sub(a, b));
  9. return 0;
  10. }

点击确定回到项目,编译、链接并运行,输出结果如下:

Congratulations! DLL is loaded!

a+b=15

a-b=5

在 main.c 中除了用 extern 关键字声明 add() 和 sub() 函数来自外部文件,还可以用 _declspec(dllimport) 标识符声明函数来自动态链接库。

为了更好的进行模块化设计,最好将 add() 和 sub() 函数的声明放在头文件中,整理后的代码如下:

dllDemo.h

  1. #ifndef _DLLDEMO_H
  2. #define _DLLDEMO_H
  3. #pragma comment(lib, "dllDemo.lib")
  4. _declspec(dllexport) int add(int, int);
  5. _declspec(dllexport) int sub(int, int);
  6. #endif

main.c

  1. #include<stdio.h>
  2. #include "dllDemo.h"
  3. int main(){
  4. int a=10, b=5;
  5. printf("a+b=%d\n", add(a, b));
  6. printf("a-b=%d\n", sub(a, b));
  7. return 0;
  8. }

显式加载

显式加载动态链接库时,需要用到 LoadLibrary() 函数,该函数的作用是将指定的可执行模块映射到调用进程的地址空间。LoadLibrary() 函数的原型声明如下所示:

HMODULE  LoadLibrary(LPCTSTR 1pFileName);

LoadLibrary() 函数不仅能够加载DLL(.dll),还可以加载可执行模块(.exe)。一般来说,当加载可执行模块时,主要是为了访问该模块内的一些资源,例如位图资源或图标资源等。LoadLibrary()
函数有一个字符串类型(LPCTSTR)的参数,该参数指定了可执行模块的名称,既可以是一个.dll文件,也可以是一个.exe文件。如果调用成功, LoadLibrary() 函数将返回所加载的那个模块的句柄。该函数的返回类型是HMODULE。 HMODULE类型和HINSTANCE类型可以通用。

当获取到动态链接库模块的句柄后,接下来就要想办法获取该动态链接库中导出函数的地址,这可以通过调用 GetProcAddress() 函数来实现。该函数用来获取DLL导出函数的 地址,其原型声明如下所示:

FARPROC  GetProcAddress(HMODULE hModule, LPCSTR 1pProcName);

可以看到,GetProcAddress函数有两个参数,其含义分别如下所述:

  • hModule:指定动态链接库模块的句柄,即 LoadLibrary() 函数的返回值。
  • 1pProcName:字符串指针,表示DLL中函数的名字。

首先创建一个工程,命名为 cDemo,添加源文件 main.c,内容如下:

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<windows.h> // 必须包含 windows.h
  4. typedef int (*FUNADDR)(); // 指向函数的指针
  5. int main(){
  6. int a=10, b=5;
  7. HINSTANCE dllDemo = LoadLibrary("dllDemo.dll");
  8. FUNADDR add, sub;
  9. if(dllDemo){
  10. add = (FUNADDR)GetProcAddress(dllDemo, "add");
  11. sub = (FUNADDR)GetProcAddress(dllDemo, "sub");
  12. }else{
  13. printf("Fail to load DLL!\n");
  14. system("pause");
  15. exit(1);
  16. }
  17. printf("a+b=%d\n", add(a, b));
  18. printf("a-b=%d\n", sub(a, b));
  19. system("pause");
  20. return 0;
  21. }

找到上节创建的 dllDemo 工程,将 debug 目录下的 dllDemo.dll 复制到当前工程目录下。注意,只需要 dllDemo.dll,不需要 dllDemo.lib。

运行程序,输出结果与上面相同。

HMODULE 类型、HINSTANCE 类型在 windows.h 中定义;LoadLibrary() 函数、GetProcAddress() 函数是Win32 API,也在
windows.h 中定义。

通过以上的例子,我们可以看到,隐式加载和显式加载这两种加载DLL的方式各有 优点,如果采用动态加载方式,那么可以在需要时才加载DLL,而隐式链接方式实现起来比较简单,在编写程序代码时就可以把链接工作做好,在程序中可以随时调用DLL导出的函数。但是,如果程序需要访问十多个DLL,如果都采用隐式链接方式加载它们的话,
那么在该程序启动时,这些DLL都需要被加载到内存中,并映射到调用进程的地址空间, 这样将加大程序的启动时间。而且,一般来说,在程序运行过程中只是在某个条件满足时才需要访问某个DLL中的某个函数,其他情况下都不需要访问这些DLL中的函数。但是这时所有的DLL都已经被加载到内存中,资源浪费是比较严重的。在这种情况下,就可以采用显式加载的方式访问DLL,在需要时才加载所需的DLL,也就是说,在需要时DLL才会被加载到内存中,并被映射到调用进程的地址空间中。有一点需要说明的是,实际上, 采用隐式链接方式访问DLL时,在程序启动时也是通过调用LoadLibrary()
函数加载该进程需要的动态链接库的。

时间: 2024-10-27 08:32:04

动态链接库DLL的加载:隐式加载(载入时加载)和显式加载(运行时加载)的相关文章

JS高阶---显式原型和隐式原型

前言: 1.函数对象即函数的prototype原型属性指向原型对象,在创建函数时便存在,默认为空Object 2.实例对象的__proto__隐式原型在实例化创建实例时产生,值等于构造函数的显式prototype原型属性的值 3.开发时程序员操作显式原型在原型prototype上添加方法,不能直接操作隐式原型__proto__(ES6之前) 大纲: [主体] (1)每个函数都有一个prototype原型属性,称之为显式原型属性或显式原型 (2)每个实例对象都有一个__prop__原型属性,称之为

C++关键字之explicit(显式)

C++ Code 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566   /* KeyWord_explicit.cpp C++关键字:explicit(显示) Author: Michael Joessy Date: 2017-06-07 Marks: 在C++程序中很少有人去使用explicit关键

HTML5 显式编排介绍

<!DOCTYPE html><html><head> <meta charset="UTF-8"> <title></title></head><body><h1>显式编排</h1><p>这里介绍显式编排</p><section> <h2>区块A</h2> <p>内容</p><

Windows Phone 8加载外部动态链接库DLL(非安装包内的)

Windows Phone 8加载外部动态链接库DLL(非安装包内的) 在<动态加载与插件化>中大概介绍了下,wp8加载非安装包的下动态链接库,这次详细梳理下. 加载外部DLL主要的原理: 通过NtCurrentTeb获得线程环境块 从线程环境块中获得进程环境块 在进程环境块中加载过得DLL链表 从链表中找到kernelbase.dll的模块句柄 从kernelbase.dll中获得LoadLibraryEx函数地址 加载指定地址下的DLL 相关的结构体: typedef struct _CL

无法加载 DLL“rasapi32.dll”: 动态链接库(DLL)初始化例程失败。的处理备注方案

网站提示   无法加载 DLL"rasapi32.dll": 动态链接库(DLL)初始化例程失败. (异常来自 HRESULT:0x8007045A). <system.net><defaultProxy><proxy usesystemdefault="false" /></defaultProxy></system.net> 初步怀疑是因为做了云加速服务造成的代理问题,自己瞎猜!反正应该管用

[转载] 动态链接库dll的 静态加载 与 动态加载

转载自:http://blog.csdn.net/youxin2012/article/details/11538491 dll 两种链接方式  : 动态链接和静态链接(链接亦称加载) 动态链接是指在生成可执行文件时不将所有程序用到的函数链接到一个文件,因为有许多函数在操作系统带的dll文件中,当程序运行时直接从操作系统中找. 而静态链接就是把所有用到的函数全部链接到exe文件中. 动态链接是只建立一个引用的接口,而真正的代码和数据存放在另外的可执行模块中,在运行时再装入: 而静态链接是把所有的

《Entity Framework 6 Recipes》中文翻译系列 (28) ------ 第五章 加载实体和导航属性之测试实体是否加载与显式加载关联实体

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-11  测试实体引用或实体集合是否加载 问题 你想测试关联实体或实体集合是否已经加载到上下文中,另外你想使用Code-First来管理数据访问. 解决方案 假设你有如图5-26所示的概念模型 图5-26 一个包含projects,managers和contractors的模型 在Visual Studio中添加一个名为Recipe11的控制台应用,并确保引用了实体框架6的库,NuGet可

引用64位dll时候出现 未能加载文件或程序集“System.Data.SQLite”或它的某一个依赖项。试图加载格式不正确的程序。

引用64位dll时候出现 未能加载文件或程序集“System.Data.SQLite”或它的某一个依赖项.试图加载格式不正确的程序. 需要在web.config增加配置 <startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0"/> </startup>

SQLite.dll混合模式程序集是针对“v2.0.50727”版的运行时生成的,在没有配置其他信息的情况下,无法在 4.0 运行时中加载该程序集。

其他信息: V5.7.4.4 Can't find the System.Data.SQLite.dll more info : 混合模式程序集是针对"v2.0.50727"版的运行时生成的,在没有配置其他信息的情况下,无法在 4.0 运行时中加载该程序集. 解决办法,web.config 添加