MinGW 是 GCC 的 Windows 版本,穩定版已經到了 4.5.2,功能和性能上很好,感覺不比 Microsoft 自家的 VC 差啊。但是 MinGW 下使用和創建 DLL 倒是要特別注意,問題主要集中在 g++ 編譯器(C++ 的 GNU 版本編譯器)對於 DLL 的函數輸入以及輸出的名稱修飾、調用協議上和 VC 編譯器是有很大區別的。
1、MinGW 如何使用一個標准的 DLL。這裡標准 DLL 指的是采用 __stdcall 調用協議、並且導出函數名稱干干淨淨,沒有函數名尾部的 @nn、沒有函數名頭部的下劃線的。MinGW 的 g++ 程序中,對於需要從 DLL 中導入的函數,聲明時必須要用 extern "C",但不要用 __declspec(dllimport),雖然 g++ 支持,但是一旦你使用,則 g++ 在鏈接時會自動強制在需要導入的函數名前加 _imp__ 前綴,導致鏈接時找不到函數名錯誤。所以,我們其實完全不需要用 __declspec(dllimport)。另一方面,gcc/g++ 編譯器遇到代碼中 __stdcall 修飾的函數名,會自動將其函數名在鏈接時設置為函數名@nn,nn 是函數參數棧字節數。另外,gcc/g++ 編譯器/鏈接器在鏈接時其實不需要 DLL 的導入庫(import lib),因為它們可以直接從 DLL 鏈接,這樣更加方便,省去了很多從 DLL 如何生成符合格式要求的 .a 導入庫等問題。只需要在 gcc/g++ 參數中加入 -Wl,--enable-stdcall-fixup -L../../Bin/ -lDLL文件名(不含.dll)即可直接從 DLL 文件本身完成鏈接。這裡要注意,-Wl參數指示 g++ 鏈接器需要采用後面的鏈接控制參數(以逗號分隔),--enable-stdcall-fixup 告訴 g++ 鏈接器需要導入的 DLL 函數的名字需要自動在尾部加上 @nn 格式的後綴,以便符合 gcc/g++ 對 __stdcall 函數名的擴展規范。-L 指定 DLL 文件所在目錄,-l 指定 DLL 文件名稱,不帶 .dll。如果不加 --enable-stdcall-fixup,gcc/g++ 總是會報鏈接錯誤,因為 gcc/g++ 將代碼中需要從 DLL 導入的函數名後面都強制加了 @nn,但是 DLL 中的函數名不帶 @nn,沒有 --enable-stdcall-fixup,很有可能就會出錯。即時不出錯,也會有很多警告,很有可能會導致程序 crash!注,在 NetBeans CDT 中,-Wl,--enable-stdcall-fixup -L -l 這樣的參數在鏈接器參數設置中指定。
2、MinGW 如何創建一個標准的 DLL。MinGW 中創建標准 DLL,應該使用 __declspec(dllexport),包括 extern "C" 等都是和 VC 一樣的。但要注意,這樣編譯鏈接生成的 DLL,導出的函數名尾部都帶有 @nn,為了要去除它們,必須在鏈接器參數設置中使用 -Wl,--kill-at,它告訴鏈接器創建 DLL 時導出的函數名尾部不要帶有 @nn。注,在 NetBeans CDT 中,上面這個參數同樣是在鏈接器參數設置中指定。
綜上,如果創建一個 DLL,同時這個 DLL 又需要導入其他 DLL 的函數,參數就可以統一為 -Wl,--kill-at,--enable-stdcall-fixup -L -l 這樣了,很方便。感覺 MinGW 確實相當強悍,跨平台,可調性很靈活,性能也很強勁,更難能可貴的是,它是一個免費而又強大的編譯器!當然,搭配 NetBeans CDT 更是相當好的一款 C/C++ 開發利器。
MinGW 創建的程序或 DLL 脫離 libgcc-xx-xx.dll 和 libstdc++-x.dll 運行庫的方法
MinGW 沿襲了 Linux 下 gcc/g++ 的習慣,編譯出的程序或者動態鏈接庫(共享庫)總是默認采用動態鏈接方式,需要系統中附帶運行時庫文件 libgcc-xx-xx.dll 和 libstdc++-xx.dll。那麼如果我們不想發布程序時還要附帶這兩個運行庫,該如何操作呢?通過參考 gcc/g++ 用戶手冊,發現只需要在編譯器或鏈接器參數設置中使用 -static-libgcc 和 -static-libstdc++ 即可實現對這兩個運行庫的靜態庫鏈接,不必附帶運行庫了。如果采用參數 -static,那麼表示所有涉及到的外部共享庫都采用靜態鏈接方式了。不過 MinGW 始終還是要依賴 msvcrt.dll 這個在 Windows 平台上的基本運行庫,但相信任何 Windows 平台都不會少了 msvcrt.dll 這個文件的,這是 Windows 必然自帶的系統文件啦。