这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » MCU » 5.动态链接库的加载

共9条 1/1 1 跳转至

5.动态链接库的加载

高工
2014-10-30 12:52:47     打赏

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

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

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

  隐式加载又叫载入时加载,指在主程序载入内存时搜索DLL,并将DLL载入内存。隐式加载也会有静态链接库的问题,如果程序稍大,加载时间就会过长,用户不能接受。

  显式加载又叫运行时加载,指主程序在运行过程中需要DLL中的函数时再加载。显式加载是将较大的程序分开加载的,程序运行时只需要将主程序载入内存,软件打开速度快,用户体验好。


  隐式加载

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


#include<stdio.h>

extern int add(int, int);  // 也可以是 _declspec(dllimport) int add(int, int);
extern int sub(int, int);  // 也可以是 _declspec(dllimport) int sub(int, int);

int main(){
    int a=10, b=5;
    printf("a+b=%d\n", add(a, b));
    printf("a-b=%d\n", sub(a, b));
    return 0;
}

 

  找到上节创建的 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")


  更改上面的代码:

#include<stdio.h>
#pragma comment(lib, "dllDemo.lib")

_declspec(dllimport) int add(int, int);
_declspec(dllimport) int sub(int, int);

int main(){
    int a=10, b=5;
    printf("a+b=%d\n", add(a, b));
    printf("a-b=%d\n", sub(a, b));
    return 0;
}

 

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

  Congratulations! DLL is loaded!

  a+b=15

  a-b=5

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

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


  dllDemo.h

#ifndef _DLLDEMO_H
#define _DLLDEMO_H

#pragma comment(lib, "dllDemo.lib")
_declspec(dllexport) int add(int, int);
_declspec(dllexport) int sub(int, int);

#endif

 main.c


#include<stdio.h>
#include "dllDemo.h"

int main(){
    int a=10, b=5;
    printf("a+b=%d\n", add(a, b));
    printf("a-b=%d\n", sub(a, b));
    return 0;
}

 

  显式加载

  显式加载动态链接库时,需要用到 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,内容如下:

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>  // 必须包含 windows.h

typedef int (*FUNADDR)();  // 指向函数的指针

int main(){
    int a=10, b=5;
   
    HINSTANCE dllDemo = LoadLibrary("dllDemo.dll");
    FUNADDR add, sub;
    if(dllDemo){
        add = (FUNADDR)GetProcAddress(dllDemo, "add");
        sub = (FUNADDR)GetProcAddress(dllDemo, "sub");
    }else{
        printf("Fail to load DLL!\n");
        system("pause");
        exit(1);
    }

    printf("a+b=%d\n", add(a, b));
    printf("a-b=%d\n", sub(a, b));

    system("pause");
    return 0;
}

 

  找到上节创建的 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() 函数加载该进程需要的动态链接库的。




专家
2021-09-29 09:49:53     打赏
2楼

123


高工
2021-09-29 21:05:00     打赏
3楼

哪里能下载到软件


专家
2021-09-30 00:01:12     打赏
4楼

谢谢分享!


工程师
2021-10-11 23:52:45     打赏
5楼

加载起来还是比较复杂的


专家
2022-07-05 22:20:29     打赏
6楼

感谢分享


专家
2022-07-05 22:47:12     打赏
7楼

感谢分享


高工
2022-07-06 00:07:15     打赏
8楼

好厉害


专家
2022-07-06 00:15:40     打赏
9楼

谢谢分享


共9条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]