这里我们省略新建项目的过程,要注意的是我们新建项目的时候选择 Dynamic Link Library

一、编译得到动态库

编译后就得了动态库,也被放在了工程目录下的bin/Debug/或者bin/Release/

win_dynamic.dll
libwin_dynamic.a

在Linux下制作动态库时,所得到只有一个***.so文件,但是制作Windows动态库时,
所得到文件会有两个:

  • win_dynamic.dll:真正的动态库
  • libwin_dynamic.aWindows 下动态库的“动态库引导文件”

这个两个文件合起来等价于***.solibwin_dynamic.a这个“动态库引导文件”也是一个静态库,
只不过这个静态库里面放的只是动态库符号解析所用基本信息,而不是库函数

  • 链接Linux下的动态库时:链接的是***.so
  • 链接Windows下的动态库时:链接的是“动态库引导文件”,而不是.dll

二、动态库加载器 - 使用动态库

根据加载动态库方式的不同,分为两种情况:

  • 通过“动态库加载器”加载
  • 在程序中调用“动态库加载函数”来加载

1. 通过“动态库加载器”加载

创建一个使用动态库的工程

File——>New——>Project———>Console application ——>...

编写测试文件:main.c

#include "caculate.h"  //包含静态库的头文件
#include <stdio.h>

int main(void)
{
    double a = 10.5;
    double b = 20.6;
    double ret = 0;

    ret = add(a, b); //加
    printf("add: ret = %f\n", ret);

    ret = sub(a, b); //减
    printf("sub: ret = %f\n", ret);

    ret = mul(a, b); //乘
    printf("mul: ret = %f\n", ret);
    ret = div(a, b);
    printf("div: ret = %f\n", ret);

    ret = power(a);  //除
    printf("power: ret = %f\n", ret);

    return 0;
}

2. 包含头文件

参考静态库使用

3. 指定要链接的“动态库引导文件”

参考静态库编译链接设置

Settings——>Compiler——>Global compiler settings——>Linker settings

4. 编译与运行

win_dynamic.dll复制到win_dynamic_test.exe可执行程序所在目录,
运行程序时,动态库加载器才能找到这个动态库,并加载到内存中

编译链接、并运行

编辑链接时,会链接“动态库引导文件”,对程序中的addsub等进行符号解析,
要不然会提示找不到addsub等函数的函数定义

编译之后就得到了win_dynamic_test.exe可执行程序,运行程序时,
“动态库加载器”会将win_dynamic_test.exe所在目录下的win_dynamic.dll动态库加载到了内存中


三、动态库加载函数 - 使用动态库

在程序中调用“动态库加载函数”来加载
Linux平台所提供动态库加载函数为dlopen、dlsym、dlclose,同样的,
Windows平台这边也有对应的“动态库加载函数”,分别是LoadLibrary、GetProcAddress、FreeLibrary

  • LoadLibrary:类比于dlopen
  • GetProcAddress:类比于dlsym
  • FreeLibrary:类比于dlclose

使用这几个函数时需要包含windows.h,类比于Linux这边的dlfcn.h


windowsLinux 的这几个函数有些不同之处:

  • dlopen、dlsym、dlclose
    Linux平台的C库函数,由libdl.so动态库提供,需要通过-ldl链接对应的动态库
  • LoadLibrary、GetProcAddress、FreeLibrary
    这几个函数是WindowsOS API,不需要指定什么-l***来链接

创建一个调用LoadLibrary等函数来加载动态库的工程

File——>New——>Project———>Console application ——>...

main.c中编写使用LoadLibrary等来加载并使用动态库的代码

#include <windows.h>
#include "caculate.h" // 可以不包含这个头文件,因为下面没有直接调用,而是通过字符串来获取函数地址
#include <stdio.h>

int main(void)
{
    double ret = 0;
    HINSTANCE handle;  //类比于Linux这边void *handle
    double (*funp)() = NULL;  //函数指针变量

    //类比于dlopen
    handle = LoadLibrary("C:\\Users\\Administrator\\Desktop\\dynamic_lib\\mycaculate.dll"); //windows编译器所用的动态库为.dll结尾

    funp = (double (*)())GetProcAddress(handle, "add");  //类比于dlsym
    ret = funp(2.3, 3.6);
    printf("%f\n", ret);

    funp = (double (*)())GetProcAddress(handle, "sub");
    ret = funp(2.3, 3.6);
    printf("%f\n", ret);

    funp = (double (*)())GetProcAddress(handle, "mul");
    ret = funp(2.3, 3.6);
    printf("%f\n", ret);

    funp = (double (*)())GetProcAddress(handle, "div1"); // 注意:div 会和 stdlib.h里面的重复声明
    ret = funp(2.3, 3.6);
    printf("%f\n", ret);

    funp = (double (*)())GetProcAddress(handle, "power");
    ret = funp(2.3);
    printf("%f\n", ret);


    FreeLibrary(handle); //类比于dlclose(handle)

    return 0;
}

指定caculate.h的路径
同上


编译运行,程序运行起来后:

  • LoadLibrary先将指定路径下的动态库打开,并加载到内存中
    当然,如果内存中已经有一份了,LoadLibrary则不会重复加载的
  • 通过GetProcAddress函数,即可获得某个动态库函数在内存中的函数指针
  • 得到动态库函数的函数指针后,就可以调用这个动态库函数了