什麼是dll文件
dll 的全稱叫:動態連接庫程序,是爲可執行文件服務的,每一個 dll 中有諸多的函數供可執行文件主體調用!
在Linux下的表現形式爲 .so 文件。dll 文件與 .exe 可執行文件同屬於 PE 文件類型,與 .exe 文件不一樣的是:dll文件須要有重定位表以及導出表
而 .exe 文件不必定須要。與 dll 對應的是靜態連接庫,一般是 .a 文件windows
爲何須要dll模塊化
怎麼連接dll函數
LoadLibrary()、GetProcAddress()和FreeLibrary()
函數#include "..\MyLib\MyLib.h" 以及 #pragma comment(lib,"MyLib.lib")
,並把相應的 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 函數對象
顯式連接調用
調用以前 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 文件放在代碼的目錄下,而後直接使用函數便可!