DLL入門指南

瞭解DLL

什麼是dll文件
dll 的全稱叫:動態連接庫程序,是爲可執行文件服務的,每一個 dll 中有諸多的函數供可執行文件主體調用!在Linux下的表現形式爲 .so 文件。dll 文件與 .exe 可執行文件同屬於 PE 文件類型,與 .exe 文件不一樣的是:dll文件須要有重定位表以及導出表而 .exe 文件不必定須要。與 dll 對應的是靜態連接庫,一般是 .a 文件windows

爲何須要dll模塊化

  • 在 Windows 中,每一個程序均可以使用某個 dll 中的函數,這有助於促進代碼重用和內存的有效使用
  • 經過使用 dll,程序能夠實現模塊化
  • 只在須要某個模塊的功能時才導入該模塊,便於主程序的快速加載
  • 能夠很是容易地將更新應用於各個模塊,而不會影響該程序的其餘部分

怎麼連接dll函數

  • 動態連接庫的顯式連接,使用 LoadLibrary()、GetProcAddress()和FreeLibrary() 函數
  • 動態連接庫的隱式連接,在代碼中添加 #include "..\MyLib\MyLib.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 代碼以下this

#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 代碼以下:code

#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("%d\n",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=%d\n",add(a,b));
        }
        if(sub != NULL){
            printf("a-b=%d\n",sub(a,b));
        }
        if(mul != NULL){
            printf("a*b=%d\n",mul(a,b));
        }
        if(div != NULL){
            printf("a/b=%d\n",div(a,b));
        }
    }else{
        printf("Load Failed!\n");
    }
    FreeLibrary(hDLL);
    system("pause");
    return 0;
}

隱式連接調用
在代碼中添加 #include "..\MyLib\MyLib.h" 以及 #pragma comment(lib,"MyLib.lib") ,並把相應的 dll 文件放在代碼的目錄下,而後直接使用函數便可!

END

相關文章
相關標籤/搜索