zoukankan      html  css  js  c++  java
  • Windows 动态链接库编程

    1、介绍

    Windows操作系统是应用最关键的操作系统,因此动态链接库也为程序员所熟悉,即使对于普通的使用者来说,很多时候也会碰到.dll结尾的文件,这就是动态链接库文件。Windows下的动态链接库可以通过参考头文件和.lib库文件进行编译,从而使得动态链接库隐式地被使用;也可以使用LoadLibrary、GetProcAddress等函数来显式调用动态链接库。

    2、语法、导入导出

    在Windows编程中,对于要使用或被使用的函数或者变量,需要使用 __declspec 关键字来声明,以告诉编译器该变量或函数不是普通的变量或函数,而是一个动态链接库的接口属性。

    如果定义一个要被其他代码使用的函数,可以写成:
    __declspec( dllexport ) int add(int a, int b);

    如果在该代码中,打算使用另外一个程序中的变量,则可以写成:
    __declspec( dllimport ) char *name;

    动态链接库通常包含一系列供其他程序使用函数,因此 declspec( dllexport ) 语法形式最为常用。如果动态库需要其他程序中的定义的全局变量,则需要在其他程序中使用导出该变量,在动态链接库中则需要使用 extern declspec( dllexport ) 将该变量声明为外部变量以便使用。

    3、链接方式

    可以以下列两种方式之一链接到(或加载)DLL:

    隐式链接
    显式链接

    隐式链接有时称为静态加载或加载时动态链接。显式链接有时称为动态加载或运行时动态链接。在隐式链接下,使用 DLL 的可执行文件链接到该 DLL 的创建者所提供的导入库(.lib 文件)。使用 DLL 的可执行文件加载时,操作系统加载此 DLL。客户端可执行文件调用 DLL 的导出函数,就好像这些函数包含在可执行文件内一样。

    在显式链接下,使用 DLL 的可执行文件必须进行函数调用以显式加载和卸载该 DLL,并访问该 DLL 的导出函数。客户端可执行文件必须通过函数指针调用导出函数。可执行文件对两种链接方法可以使用同一个 DLL。另外,由于一个可执行文件可隐式链接到某个 DLL,而另一个可显式附加到此 DLL,故这些机制不是互斥的。

    4、隐式链接

    隐式链接动态链接库比较简单,不予详述。

    5、显式链接API函数

    显式链接主要涉及到3个API函数( LoadLibrary , GetProcAddress 和 FreeLibrary ),要使用这些函数包含windows.h头文件即可。

    (1)HINSTANCE LoadLibrary(LPCSTR lpLibFileName);

    该函数用来加载指定动态库文件,并且返回句柄。

    参数lpLibFileName为动态链接库的名称。Windows 首先搜索“已知 DLL”,如 Kernel32.dll 和 User32.dll。然后按下列顺序搜索 DLL:

    1、当前进程的可执行模块所在的目录。
    2、当前目录。
    3、Windows 系统目录。GetSystemDirectory 函数检索此目录的路径。
    4、Windows 目录。GetWindowsDirectory 函数检索此目录的路径。
    5、PATH 环境变量中列出的目录。

    (2)FARPROC GetProcAddress (HMODULE hModule, LPCSTR lpProcName);

    函数GetProcAddress 用来获取 DLL 导出函数的地址。返回由lpProcName指定的变量或函数指针。

    参数hModule为已经加载的动态库文件的句柄。

    参数lpProcName为要调用的变量或函数名称。

    (3)BOOL FreeLibrary(HMODULE hModule);
    从内存中释放hModule所代表的动态链接库。

    (4)如果发生错误,可以调用GetLastError()函数或去错误代码。

    6、显示链接举例

    (1)动态库文件代码:dll_demo.c

    1 #include <stdio.h>
    2 
    3 __declspec( dllexport ) int add(int a, int b)
    4 {
    5 printf("calling add
    ");
    6 return a + b;
    7 }


    该文件中的add()函数计算两个整数之和,并且返回之前打印提示字符串。函数使用 __declspec( dllexport ) 语法来说明函数add(int a, int b)要被导出。

    (2)客户端事例代码:main.c

     1 #include <stdio.h>
     2 #include <windows.h>
     3 
     4 int main (int argc, char *argv[])
     5 {
     6 int a = 10, b = 20;
     7 int c = 0;
     8 HINSTANCE   hInstLibrary   =   NULL;
     9 int (*add)();
    10 
    11 printf ("Load DLL file
    ");
    12 if ((hInstLibrary = LoadLibrary(L"dll_demo.dll")) == NULL)
    13 {
    14    printf ("***LoadLibrary ERROR: %d.
    ", GetLastError());
    15    return 1;
    16 }
    17 if((add = (int (*)())GetProcAddress(hInstLibrary, "add")) == NULL) {
    18    printf ("***GetProcAddress ERROR: %d.
    ", GetLastError());
    19    return 1;
    20 }
    21 
    22 c = add(a, b);
    23 printf("%d + %d = %d
    ", a, b, c);
    24 
    25 FreeLibrary(hInstLibrary);
    26 return 0;
    27 }


    程序利用LoadLibrary函数加载动态链接dll_demo.dll,利用FreeLibrary关闭句柄,利用GetLastError()获取错误代码,利用GetProcAddress定位共享库中的add函数,然后调用该函数执行加法计算,并打印结果。

    (3)编译与运行

    编译共享库:

    在VS.Net中创建一个动态链接库项目,名称为dll_demo,加入文件dll_demo.c,编译后生成dll_demo.dll文件。

    编译事例程序:

    在VS.Net中创建一个动态链接库项目,名称为dll_main,加入文件main.c,编译后生成dll_main.exe可以执行文件。

    运行:

    将 dll_demo.dll 和 dll_main.exe 放在同一个目录下,然后双击运行 dll_main.exe。

    输出:

    Load DLL file
    calling add
    10 + 20 = 30

    7、调用动态链接库中的变量

    也可以使用动态链接库中的变量。例如,在动态链接库中定义:

    __declspec( dllexport ) int num = 100;

    那么可以在事例程序中这样调用:

    int *d;
    d = (int *)GetProcAddress(hInstLibrary, "num");
    printf("num = %d ", *d);

  • 相关阅读:
    centos通过yum安装mongodb
    js基于另一个数组排序数组
    centos 7 安装emule客户端
    typescript中interface和type的区别
    nodejs安装管理工具nvm的安装和使用
    PM2的参数配置
    centOS添加ipv6支持(仅限已分配ipv6地址和网关)
    linux执行计划任务at命令
    mysql中获取本月第一天、本月最后一天、上月第一天、上月最后一天等等
    win10子系统ubuntu内的nginx启动问题
  • 原文地址:https://www.cnblogs.com/xiaocaocao/p/8451489.html
Copyright © 2011-2022 走看看