1. 程式人生 > >【WIN32】CMake的學習總結 4——深入動態庫

【WIN32】CMake的學習總結 4——深入動態庫

上回講述了CMake使用動態庫與使用靜態庫之間並沒有太大的區別,並講述了學習期間碰到的問題:無法正確生成動態庫DLL,導致使用動態庫的test測試專案無法正確編譯;

【 學習時遇到的疑問 】

既然已經include_directories指定標頭檔案的搜尋路徑,標頭檔案已經宣告,使用動態庫為毛還要lib中的宣告?

宣告在標頭檔案是沒錯的,所以需要include_directories來指定標頭檔案的搜尋路徑;但是lib也並不包含函式的宣告,只是dll中的函式的列表而已,這一點我理解錯了!
在此將動態庫與靜態庫之間的區別做一個對比,分析錯誤的原因:
部分資料來自:

http://www.cppfans.org/1394.html

動態庫與靜態庫的對比

靜態庫lib

編譯靜態庫:生成的是一個lib檔案(static lib);

對於靜態庫lib的呼叫者:
編譯時:連結使用lib檔案(這個過程可以理解為:連結並把函式的實現一起打包進執行檔案中)。
執行時:則不需要則不需要lib檔案;

注:靜態庫的lib:裡面其實就是原始檔函式的實現;

動態庫dll

動態庫編譯之後有3個檔案:.dll,.lib(dynamic lib),.exp;
其中lib只是dll的附屬品,是dll匯出的函式列表檔案

對於動態庫dll的呼叫者:
編譯時:需要lib檔案,並將匯出函式列表打包進可執行檔案中,此過程並不需要dll檔案;
執行時:需要dll檔案,並根據匯出函式列表動態連結dll檔案,此過程不需要lib檔案,但不能缺少dll檔案,否則可執行檔案無法執行;

往下繼續對動態庫dll作更深入的描述:

對於動態庫dll的理解不能按照lib的思維去理解,lib只是個只編譯但未連結的obj檔案的集合而已;而dll是應用程式的擴充套件,屬於半個可執行檔案,只是它不能自己執行而已;既然如此,我們就不能好像lib那樣去編譯dll( so, 按照lib的方式編譯dll,你知道為什麼編譯的動態庫不完整了吧);

因此我們可以站在exe的角度去理解dll。
其實dll和exe是幾乎完全一樣的,唯一的不一樣就是exe的入口函式式WinMain函式(console程式是main函式),而dll是DllMain函式,其他完全是一樣的;

然而dll建立的過程也比較簡單,唯一麻煩的就是需要定義匯出函式介面

在此我們看到了dll的3個概念:匯出的函式列表的檔案(dynamic lib)匯出函式介面入口函式,歸納起來實際就是:入口函式和匯出函式2種;

入口函式:

匯出函式:

匯出函式:供呼叫dll的程式(如exe)使用,因此使用匯出函式的列表檔案(dynamic lib)可呼叫dll中所有匯出的函式;

匯出方式一:採用模組定義檔案宣告(.def)

  1. 檔案第一句必須是LIBRARY語句。
    此語句將.def檔案標識為屬於dll,LIBRARY語句的後面是dll名稱;聯結器將此名稱放到dll的到入庫中;
  2. EXPORTS語句列出名稱;
    可能的話還會列出dll匯出函式的序號值。通過函式名的後面加上@符合一個數字,給函式分配序號值。
    當指定序號值時,序號值的範圍必須是從1到N,其中N是dll倒數函式的個數。
  3. 註釋語句:在語句前面加分好“;”
  4. 例子如下:
;DLLTest.def: Declares the module parameters for the dll.
LIBRARY "DLLTest"
EXPORTS
    add @1
    fun @2

匯出方式二:_declspec(dllexport)匯出方式
《【WIN32】CMake的學習總結 3——動態庫》最後對xmath.h的修改,就是使用這種方式的;

為了防止名字改編,將extern “C” _declspec(dllexport)語句放在想匯出函式的宣告前面;
例如:extern “C” _declspec(dllexport) int add(int a, int b);

注:如果要進行函式的匯入則dllexport換成dllimport即可,
例如:extern “C” _declspec(dllimport)

至此,我們已經找到上一回後面的提出的問題了!哈哈哈~~~~

上一回後面的提出的疑問如下:

  1. 為什麼要作這樣的修改才能正確編譯動態庫dll呢?
  2. 其中的奧妙是什麼呢?
  3. 正確編譯DLL動態庫還有其他什麼方法呢?