動態連結函式庫(Dynamic Linking Libraries,DLLs)

DLL ,我想大家都不陌生
不管你是否搞程設的,都一定對它有印象(某遊戲出現錯誤之類)
這次就來了解一下
我挑著重點說,其他說明還是參考最底下的連結吧!他們寫的比較好~
(以下大多是引用)

DLL的存在有何意義?
1 、 有 效 率 的 重 複 使 用 程 式 碼
2 、 區 分 程 式 碼
3 、 節 省 記 憶 體 的 使 用 量
4 、 將 程 式 推 向 國 際 舞 台

而程設人員常聽到的COM(Component Object Model),則是 DLL 的擴充(進階)使用

這兩者都牽扯到程設新手困惑的名詞之一「介面(Interface)或稱為虛擬繼承

由於是共用記憶體,又會牽扯到一個名詞「相 對 虛 擬 位 址 (Relative Virtual Address ,RVA)
會特別強調這點,是因為我犯下一個很蠢的舉動,後面會說

DLL產生的同時會伴有一個對應的LIB

要給別人用的的程式,需要作輸出(Export)的動作,反之需要作輸入(Import)的動作



OK!讀萬卷書不如行萬里路!就來做出一個DLL來吧!
1 、 開啟DLL專案,以VC8.0為例
        「檔案 > 新增 > 專案 > Visual C++ > Win32 > Win32專案 > 應用程式類型 > DLL > 完成!」
2 、 console 和 window 都有城市的進入點 main() 和 WinMain()
        DLL的進入點則為 DLLMain()
3 、 輸出,再需要輸出的函式前面加上「__declspec(dllexport)」即可,例如:

 __declspec(dllexport) void Function(void);

        也可以輸出一個類別(Class)

 class __declspec(dllexport) MyClass : public BaseClass{ … …};

當然別忘記要實作這些函數啊!
做個簡單的範例:

#include <iostream>
#include <Windows.h>
using namespace std;

 // 先宣告函式
 __declspec(dllexport) int WhoBig(int a, int b);
 __declspec(dllexport) int WhoSmall(int a, int b);

 // 進入點
 int WINAPI DLLMain(HINSTANCE hinst, unsigned long reason, void* lpReserved)
 {
     return 1;
 }

 // 實作函式
 
int WhoBig(int a, int b)
 { return ( a > b ) ? ( a ):( b ); }

 int WhoSmall(int a, int b)
 {
return ( a < b ) ? ( a ):( b ); }

嗯!編譯無誤之後就可以得到DLL和LIB了!



接著來看看如
使用~
使用該DLL裡面的函式可分為兩大類:
1 、 隱式連結(Implicitly Link)

優點:
1 、 靜態載入方式所使用到的這個DLL會在應用程式執行時載入
        然後就可以呼叫出所有由DLL中匯出的函式
        就好像是包含在程式中一般。

2 、 動作較為簡單,載入的方法由編譯器負責處理,咱們不須動腦筋。

缺點:
1 、 當這個程式靜態載入方式所使用到的這個DLL不存在時
        這個程式在開始時就出現無法找到DLL的訊息而 導致應用程式無執行。

2 、 編譯時需要加入額外的import library。

3 、 若是要載入的DLLs一多,載入應用程式的速度會便慢。

4 、 若遇到不同品牌 的C++編譯器時
        靜態載入可就沒有這麼簡單處理了
        因為當函式經過Calling Conventions的處理後
        若要使用其他品牌編譯器所致造出的DLL須得大動干戈才行。


2 、 顯式連結(Explicit Linking)


優點:
1 、DLL只要需要時才會載入到記憶體中
       可以更有效的使用記憶體。

2 、 應用程式載入的速度較使用隱式鏈結時快
        因為當程式開始載入時並不需要把DLL給載入到行程中。

3 、 編譯時不須額外的import library檔。

4 、 讓我們可以更清楚DLL的載入流程。

缺點:
就是得要自行連結,自然要多點code囉!




靜態連結範例

1 、連結 lib
可以使用 #program 指令連結,或者編譯器連結

2 、宣告
export 時使用…

__declspec(dllexport)
int WhoBig(int a, int b);

import時…
__declspec(dllimport)
int WhoBig(int a, int b);


3 、放置DLL
把 test.dll 放在適當的地方就大功告成了!

之後就可以當一般函式使用了~


  // 連結 lib
  #pragma commment(lib, "test.dll");

 // 宣告
 
__declspec(dllexport) int WhoBig(int a, int b);
  __declspec(dllexport) int WhoSmall(int a, int b);

 // …任意使用



小技巧

如果每次更改都要對 import / export 分別修改,那豈不煩死人?

 // test.h
 #ifdef DLL_EXPORT
 #define DLLACT __declspec(dllexport)
 #else
 #define DLLACT __declspec(dllimport)
 #endif

 // 宣告
 
DLLACT int WhoBig(int a, int b);
 
DLLACT int WhoSmall(int a, int b);


聰明的你應該看出來了吧!
在製作dll的時候也只要在 include 這個 .h 檔之前定義
DLL_EXPORT 就可以了!
DLL_EXPORT 這個名稱隨便你設
一般使用者也一樣 inlude ,連結 lib 就可以使用,方便吧?





Name Mangling
似乎沒有一個正式的翻譯?總之就是針對函數名稱做點處理的意思!
在不同編譯器底下,處理出來的結果都不同,這就是隱式連結的缺點之一
例如:
int Func(int X);
int Func(float X);
void Func(double *d);

使用 C++Builder 3.0 所編譯出來的函式名稱為:
@Func$qf
@Func$qi
@Func$qpd

而使用 Visual C++ 6.0 所編譯出來的函式名稱為:
?Func@@YAHH@Z
?Func@@YAHM@Z
?Func@@YAXPAN@Z


這麼做主要是因為C++提供了多載(overload)
以往C風格的處理方式就不敷使用了!
當然,我們可以抑制 Name Mangling
在需要輸出的函式前面加上 extern "C" 即可
例如:

 extern "C" __declspec(dllexport) int WhoBig(int a, int b);

參考:

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 變更 )

Twitter picture

You are commenting using your Twitter account. Log Out / 變更 )

Facebook照片

You are commenting using your Facebook account. Log Out / 變更 )

Google+ photo

You are commenting using your Google+ account. Log Out / 變更 )

連結到 %s