2009年7月25日 星期六

Hello DLL - 2初嚐Win32DLL

專案名稱:VCDll
目的:
一、將自己以前作的Win32 Static Link Libary,變成Win32 Dynamic-Link Library(DLL)
二、示範將一些標準函式庫輸出
(也可以解決OpenCV在BCB6下的問題 >"< => 找不到cvhaartraining.dll的run-time error)

方法:

1.用精靈新增一Win32 Dynamic-Link Library專案
選擇「A DLL that exports some symbols」 (因為會自動產生所需檔案 :p )

2.將 VCLib.h 和 VCLib.lib (於build VCLib專案後產生)
複製到VCDLL專案的目錄下,並加入專案中(使用方案總管)

3.在stdafx.h中 #include "VCLib.h" 及一些標準函式庫

4.常見的.h寫法
(VCDLL是這個例子的專案名稱,其它的開發環境或專案可用能的是 WIN32_API)

#ifdef VCDLL_EXPORTS
#define VCDLL_API __declspec(dllexport) //export dll時用
#else
#define VCDLL_API __declspec(dllimport) //import dll時用
#endif

a.當include這個.h檔,要export dll時
編譯器會自動加上相關的編譯參數而#define VCDLL_EXPORTS
(VC "專案屬性 =>C/C++ =>前置處理器" 中的設定 或 BCB的-WD編譯參數)
以使用 __declspec(dllexport)

b.當include這個.h檔,要import dll時
因為沒有#define VCDLL_EXPORTS,故使用 __declspec(dllimport)

6. 將欲export的函式宣告放在 extern "C" { ... }
Q:啥米係 extern "C" ? (自問自答中…)
A:編譯時將函式自動更改成不同名(依compiler而不同),稱為『name mangling
在函式的宣告前加上extern "C"這個修飾詞,
強制將函式名稱以C語言的形態重現,而非以C++語言的形態出現

如此一來就可以防止name mangling造成 :
在其它compiler要import同一個dll時會出問題 (例如VC下的dll/lib 要給 BCB用 =__=")


但是要注意,多載函式不能加上extern "C"這個修飾詞
(不然的話,有好幾個函式都同名,要link哪一個?
overloading function在compile-time就是用 name mangling 的方法實作,以便link時不會出錯)

7.在VCDll.cpp中加入程式碼,為了展示dll的實作
只將函式名稱更改或變大寫後,然後呼叫原函式
(將好幾顆水餃包成一個大包子,好吃嗎?)


註:除了上述步驟,其它的參考程式碼皆為VC的專案精靈產生
VC展示了:匯出 "變數"、"函式"、"類別建構式" 的範例程式碼
而在//---Tony----註解後的部分是自行加入的程式碼

ps2. 很久以前就想買了,但那時的大學生活太精彩了,沒錢也沒閒(一不小心就透露年紀了)
ps3. 雖然也出了好一陣子,但對一個上班族來說還是有點貴,有小錢但更沒閒
ps4. 退休時再來玩吧…
嗯…離題了

8. Build專案後可以得到VCDll.lib及VCDll.dll (怎麼用?下回再分解…)
一般在VS開發程式的習慣是:改好程式碼後就按[綠色小三角] (開始偵錯(F5))
此時VS會Build,當沒有error時執行build好的「執行檔」(.exe)

可是這個專案的「成品」不是.exe,而是 .dll及 .lib,
所以VS會跳一個視窗出來叫你選一個「偵錯工作階段時」的可執行檔

為了避免麻煩還要取消視窗,請忍住不要去點[播放]鍵,而直接按[F7]來建置(Build)方案
不然「哇辛卡北媚,跪閃關…」的歌聲可能真的會從喇叭傳出來…

Hello DLL - 1與函式庫的第一次

開發環境:
VS 2008 Standard 繁中 SP1 (9.0.30729.1 SP)
XP pro sp3

方案內容:
實作自行開發的Static Link Libary靜態連結函式庫專案,
以及使用此函式庫的另一個Win32主控台專案

1.VCLib專案定義函式原型(.h)並實作(.lib),以供其它程式使用
(Compile時用到.h Link時用到.lib)

2.TestStaticLib專案使用VCLib專案中的函式,如同使用標準函式庫一樣
而其中的差異是:標準函式庫的路徑,IDE(開發環境)已經預設好了(在安裝時)
但使用自行開發的函式庫.h及.lib時,檔案的路徑需在專案屬性中手動加入

使用標準函式庫
#include
....
void main(void)
{.... printf("");}

<對照>
使用自己的函式庫
#include
...
void main(void)
{.... VCLib_MyFunction();}
//--------------------------------------------------------------------------------
實際操作:

一、使用VC的專案精靈,產生VCLib專案

新增Win32專案=>選Static Static Library,不勾「Pre-Compiled header」及「MFC support」
得到一空的專案,自行編寫及加入.cpp及.h

專案名稱:VCLib
主要原始檔:VCLib.cpp, VCLib.h
主要函式:int AddMethod(int a, int b)
用途:將二個整數相加的值回傳


二、在方案中新增一新專案TestStaticLib
類型:主控台應用程式,可勾選「先行編譯標頭檔」
(以後需要include的函式庫都加在stdafx.h中即可)

專案名稱:TestStaticLib
主要原始檔:TestStaticLib.cpp
目的:#include "VCLib.h"的int AddMethod(int a, int b)來用

//--------------------------------------------------------------------------------
Tony :錯誤訊息及改進方法

fatal error C1083: 無法開啟包含檔案: 'VCLib.h': No such file or directory

若複製一份VCLib.h檔至TestStaticLib專案目錄下,然後用#include "VCLib.h"簡單可行
(只在方案總管內加入.h檔,在Compile或Build時無效的,因為Compiler
仍不知道.h的路徑,方案總管對標頭檔的增減只算是VS IDE的一個檔案管理工具罷了)

但要更改VCLib.h時,要手動做一次上述動作,既麻煩又常會忘。
故改用 #include
然後在[專案屬性]設定好路徑
C/C++ => 一般 => 其它include目錄
加入 "$(SolutionDir)VCLib\" 這個巨集的相對路徑
以後就只維護一份VCLib.h即可
--------------------------------------------------------------------------------
TestStaticLib.obj : error LNK2019: 無法解析的外部符號
"int __cdecl AddMethod(int,int)" (?AddMethod@@YAHHH@Z) 在函式 _wmain 中被參考

TestStaticLib =>專案屬性=>連結器=>其它相依性=>加入VCLib.lib
(雖然已經加入路徑了,但VC卻不知要用它 =__=)
-------------------------------------------------------------------------------
LINK : fatal error LNK1104: 無法開啟檔案 'VCLib.lib'

TestStaticLib =>專案屬性=>連結器=>將VCLib.lib的路徑加入
此方案中因為VCLib專案的輸出檔預設為$(OutDir)\$(ProjectName).lib
故路徑亦可用$(OutDir)
--------------------------------------------------------------------------------

Visual Studio方案相關:

因為VCLib專案要先產生VCLib.lib,才能供TestStaticLib用
故方案屬性=>專案相依性=> 專案TestStaticLib要勾選"相依於VCLib"
如此在"建置方案"時,就會有對的順序,不致有錯誤訊息產生
--------------------------------------------------------------------------------
設定路徑小技巧:
在選擇如Include路徑的對話方塊,右下角有個[巨集]按鈕,可提供
一些相對路徑的巨集名稱,例如:$(OutDir)
使用相對路徑時,就算將整個方案的資料夾搬到別的路徑或電腦下
仍然可以正常Build (但安裝SDK時的相對路徑也不可被手動更改)

程式碼範例還在找地方上傳…