了解DLL

什么是dll文件
dll 的全称叫:动态链接库程序,是为可执行文件服务的,每个 dll 中有诸多的函数供可执行文件主体调用!在Linux下的表现形式为 .so 文件。dll 文件与 .exe 可执行文件同属于 PE 文件类型,与 .exe 文件不同的是:dll文件需要有重定位表以及导出表而 .exe 文件不一定需要。与 dll 对应的是静态链接库,通常是 .a 文件

为什么需要dll

  • 在 Windows 中,每个程序都可以使用某个 dll 中的函数,这有助于促进代码重用和内存的有效使用
  • 通过使用 dll,程序可以实现模块化
  • 只在需要某个模块的功能时才导入该模块,便于主程序的快速加载
  • 可以非常容易地将更新应用于各个模块,而不会影响该程序的其他部分

怎么链接dll

  • 动态链接库的显式链接,使用 LoadLibrary()、GetProcAddress()和FreeLibrary() 函数
  • 动态链接库的隐式链接,在代码中添加 #include "..MyLibMyLib.h" 以及 #pragma comment(lib,"MyLib.lib") ,并把相应的 dll 文件放在代码的目录下

编写DLL

编写前的准备
dll 的编写特别要注意你的编译器选择,我目前也只用了两个主流的编译器做过测试,一个是微软 VisualStudio 自带的 MSVC编译器,另一个是 GNU 在 Windows 上的 MinGW编译器
如果你使用 MinGW,那么和写普通程序区别不大;但如果你使用 MSVC 的话,你就需要注意一些固定的格式,以及一些宏定义

MSVC版
直接新建一个 dll 文件项目,先写一个头文件,把一些变量和函数的定义写好,这里有兴趣的话可以了解一下 #ifdef 以及 extern "C" __declspec(dllimport),头文件 header.h 代码如下

#ifdef MYLIBAPI
#define MYLIBAPI extern "C" __declspec(dllexport)
#else
#define MYLIBAPI extern "C" __declspec(dllimport)
#endif
MYLIBAPI int res;
MYLIBAPI int myadd(int num1,int num2);

然后是主要的功能代码,mydll.c 代码如下

#include <windows.h>
#define MYLIBAPI extern "C" __declspec(dllimport)
#include "header.h"
int res;

int myadd(int num1,int num2){
    res = num1 + num2;
    return res;
}

注意事项:
extern "C" 主要是排除 C++ 编译的干扰,C++ 编译某个函数后会变成 func@ 的形式,不方便主程序根据函数名调用
__declspec(dllimport) 从其它动态库中声明导入函数、类、对象等供本动态库或exe文件使用,在没有全局静态变量时可以不使用该关键字
__declspec(dllexport) 声明为导出函数、类、对象等供其它程序调用,如果不使用该关键字导出 dll 函数,则需要 .def 文件

MinGW版
使用 MinGW 编译器的话和写普通程序类似,只写需要用到的函数即可,不需要 main 主函数,然后编译成 dll 文件即可。编译一句搞定 gcc math.c -shared -o math.dll -Wl,--out-implib,math.lib,--output-def,math.def,还能生成 .lib 以及 .def 文件,用 C++ 的话可能还需要 --kill-at,mydll.c 代码如下:

#include<stdio.h>
int add(int a,int b){
    return a+b;
}
int sub(int a,int b){
    return a-b;
}
int mul(int a,int b){
    return a*b;
}
int div(int a,int b){
    return a/b;
}
// gcc math.c -shared -o math.dll -Wl,--out-implib,math.lib,--output-def,math.def

注意: dll 程序其实也是有入口函数的-DllMain,操作系统在调用 LoadLibrary() 线程的上下文中调用此入口函数,并且入口函数中通常会说明该 dll 被调用的方式!除非有特殊需求,一般不需要写 DllMain 函数

调用DLL

显式链接调用
调用之前 MSVC 生成的 mydll_vc.dll 中的函数

#include <stdio.h>
#include <windows.h>
int main(){
    HINSTANCE hMyDLL = NULL; // typedef HINSTANCE HMODULE;
    int (*MyAdd)(int,int);
    hMyDLL = LoadLibrary("mydll_vc.dll");
    if(hMyDLL == NULL){
        printf("Can not open this file!n");
    }
    MyAdd = (int (*)(int,int))GetProcAddress(hMyDLL,"myadd");
    printf("%dn",MyAdd(99,999));
    return 0;
}

调用之前 MinGW 生成的 mydll_gcc.dll 中的函数

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
typedef int (*AddFunc)(int,int);
typedef int (*SubFunc)(int,int);
typedef int (*MulFunc)(int,int);
typedef int (*DivFunc)(int,int);
int main(){
    int a=66,b=6;
    HMODULE hDLL = LoadLibrary("mydll_gcc.dll");
    if(hDLL != NULL){
        AddFunc add = (AddFunc)GetProcAddress(hDLL, "add");
        SubFunc sub = (SubFunc)GetProcAddress(hDLL, "sub");
        MulFunc mul = (MulFunc)GetProcAddress(hDLL, "mul");
        DivFunc div = (DivFunc)GetProcAddress(hDLL, "div");
        
        if(add != NULL){
            printf("a+b=%dn",add(a,b));
        }
        if(sub != NULL){
            printf("a-b=%dn",sub(a,b));
        }
        if(mul != NULL){
            printf("a*b=%dn",mul(a,b));
        }
        if(div != NULL){
            printf("a/b=%dn",div(a,b));
        }
    }else{
        printf("Load Failed!n");
    }
    FreeLibrary(hDLL);
    system("pause");
    return 0;
}

隐式链接调用
在代码中添加 #include "..MyLibMyLib.h" 以及 #pragma comment(lib,"MyLib.lib") ,并把相应的 dll 文件放在代码的目录下,然后直接使用函数即可!

END

本文固定链接: http://www.js-code.com/cpp/cpp_60861.html