Download presentation
Presentation is loading. Please wait.
Published byErika Miller Modified 6年之前
1
第二十九章 DLL / LIB函式庫開發 當我們開發程式到一個階段之後,我們一定會希望各個Component的程式碼可以分開的越清楚越好。而這一章最主要就是要告訴各位讀者,我們常在Windows系統中看到的dll或是lib的檔案該怎麼實作?做出這樣的library我們又該如何運用?為什麼使用dll或是lib有利於我們開發程式?以上這些疑問都將會在這一章中得到解答。
2
大綱 29-1. DLL動態函式庫開發 29-2. LIB靜態函式庫開發 本章習題
3
29-1. DLL動態函式庫開發 相信各位一定常常會遇到一種情況,就是常常在執行某些應用程式時,系統會出現『無法找到動態連結程式庫xxx.dll』,接著這個應用程式將會在你按下確定時一起被關閉。我們這一小節要提到的東西就是這個『DLL』。 到底dll是什麼東西?這個東西他又有什麼樣的用途呢?DLL的原文意思是『Dynamic Link Library』,也就是動態連結函式庫。一般小型的應用程式很少會用到這種所謂的DLL動態函式庫,但是對於一個大型應用程式(如BCB、Office、Windows等等),使用DLL動態函式庫可以降低許多Memory的使用量,也有助於各個Component的獨立,更有利於後續版本的維護及開發。
4
29-1. DLL動態函式庫開發 假設某個應用程式有使用到某個dll檔案,在開啟這個應用程式時,應用程式並不會馬上將dll這個函式庫load進入Memory中,只是將『載入點』紀錄下來供需要時才使用。若是在這次執行應用程式的過程中,都不需要執行到dll函式庫內所提供的功能,那在這次執行程式的過程中就減少了一個dll的Memory空間!若是有需要這個dll所提供的功能時,應用程式才會即時的將這個dll所需要的資源load到Memory中,讓使用者使用。 DLL函式庫還有一個關於記憶體方面的特點,那就是如果有超過兩個以上的應用程式使用到同一個dll,在記憶體中並不會吃掉兩倍以上的記憶體空間,不管幾個程式會用到,記憶體中永遠就只有放這麼一份dll函式庫的資源。根據上面的說法,DLL真的可以降低執行的記憶體使用量!
5
29-1. DLL動態函式庫開發 除了記憶體方面的優勢,當我們在開發DLL的時候,我們一定是根據某個功能開發的,所以當我們現在要開發一個大型的應用程式,我們就可以先利用軟體工程的技術好好的分析一下即將要開發的應用程式,可以開始分Component,將這些Component的Input以及Output都定義清楚,並且發包出去給各個程式設計師。這時候,程式設計師只需要將接到的工作都寫成DLL檔案,這樣既方便最後的整合,也將各個功能的程式碼獨立出來。 既然DLL有以上我們所說的優點,那是不是表示,當我們在開發一套軟體的後續開發或是維護時,也因為之前分工的很清楚,所以維護容易,可以減少維護軟體的人力與物力。
6
29-1. DLL動態函式庫開發 寫DLL需要注意的事情: 如何寫『entry point』
這個entry point就是當有程式呼叫到dll檔案時的切入點。 利用BCB來寫DLL動態函式庫有兩種entry point的寫法, 第一種是使用BCB的entry point 第二種則是使用Microsoft Visual C++的entry point 一般來說,既然要寫一個DLL動態函式庫一定就是希望可以讓多個應用程式共同使用它,這時候如果這些要用到這個DLL的應用程式的發展環境都在BCB下的話,你選擇哪一種DLL的entry point格式都沒有關係,但是如果你這個DLL檔案有可能讓在Visual C++下發展的應用程式使用到的話,我們還是比較建議使用Microsoft Visual C++格式的entry point會比較好。
7
29-1. DLL動態函式庫開發 範例29-1:DLL動態函式庫開發(BCB Style DLL) 範例說明
在範例29-1中,我們最主要是使用BCB的Entry Point當作切入點來實作該DLL函式庫,而這個函式庫所要表現出來的功能就是從m加到n的總合為多少。底下我們就一步一步帶領讀者開始練習DLL的實作範例。 Step 1:建立一個dll的開發專案 開一個新的專案,並且選擇DLL這個圖示。接著會出現一個對話窗,讓我們決定要使用C還是C++開發該DLL元件,也讓我們選擇是不是要使用VCL、CLX、Multi Thread來開發,或是要改用VC++的Entry Point。
8
29-1. DLL動態函式庫開發 範例29-1:DLL動態函式庫開發(BCB Style DLL)
Step 2:撰寫主要程式碼 當我們按下該對話窗的OK之後,BCB就會自動幫我們產生一些必要的Code,這些Code包括了最重要的Entry Point,接下來我們就在自動產生的這段程式碼的最後加入以下的程式碼: long __declspec(dllexport) __stdcall BCBType_Count(int m, int n) { long sum = 0; for (m ; m <= n ; m++) sum += m; return sum; }
9
29-1. DLL動態函式庫開發 範例29-1:DLL動態函式庫開發(BCB Style DLL)
以上的Code,有一個比較特別的就是『__declspec(dllexport)』以及『__stdcall』這樣的宣告方式。 『__declspec(dllexport)』讓Compiler知道現在要寫的是一個DLL檔案。 『__stdcall』則是一種函式的呼叫方式。 在BCB中有四種不同的方式來呼叫函式,分別是『C』、『Register』、『Pascal』、以及『Standard call』。可以透過BCB專案設定中的『Advanced Compiler』這個Page修改預設的呼叫方法,預設是使用『C』的呼叫方式。
10
29-1. DLL動態函式庫開發 範例29-1:DLL動態函式庫開發(BCB Style DLL)
很不巧的,DLL這種動態函式庫需要『Standard Call』來呼叫才行的通,所以我們就必須要做些許的修改才能運作。在這邊總共有兩種方法可以採用。 第一種就是修改專案設定,將Calling convention改成Standard call。 另一種方法就是在函式宣告的部分加入『__stdcall』這個修飾詞。以筆者的習慣來說,筆者比較喜歡第二種方法,各位讀者可以兩種都嘗試看看,再根據習慣選擇一種即可。
11
29-1. DLL動態函式庫開發 範例29-1:DLL動態函式庫開發(BCB Style DLL) Step 3: 編譯前的設定
當完成以上的說明以及程式碼的設計後,接下來就是要準備編譯這個dll函式庫了,但是在編譯之前有一件事情需要設定,那就是必須要讓該專案可以產生一個『LIB』的檔案出來。這時候讀者一定會覺得怪怪的,我們現在不是在開發DLL嗎?怎麼又跟LIB扯上關係了,其實最主要是為了讓程式設計師可以使用該DLL來開發程式才需要建立這個LIB檔案。當一個程式設計師要利用某個dll所提供的功能時,一定要將該dll import到開發的專案中(在範例29-3會介紹),這時候我們需要import的就是這個LIB檔案,這也是為什麼在這邊要很強調一定要能讓BCB自動產生LIB這個檔案的重要性了!
12
29-1. DLL動態函式庫開發 範例29-1:DLL動態函式庫開發(BCB Style DLL) Step 3: 編譯前的設定 (2)
一般來說,在我們建立一個dll專案時,BCB應該已經幫我們設定好會自動產生屬於該dll的lib檔案,可是如果天不從人願或是當天剛好犯沖,再怎麼弄就是一直沒有LIB的檔案出現,這時候就要請大家看一下專案設定中『Linker』這個Page中的『Generate import library』有沒有被選取,如果沒有,就趕快將這個選項選起來吧!選完之後按一下存檔,重新Build該專案就可以了。
13
29-1. DLL動態函式庫開發 範例29-1:DLL動態函式庫開發(BCB Style DLL) Step 4: 編譯dll
在這一步各位讀者千萬不要太激動就直接按下『F9』或是點一下『Run』的按鈕直接執行,不然一定會得到一個錯誤訊息,就只因為大家太激動啦!別激動,慢慢看下去就知道了! 因為要開發一個dll專案主要是將dll和lib這兩個檔案製作出來,而不是要去『執行』這個專案,就算我們強迫他執行,也是無法跑出該有的dll和lib這兩個檔案。在這邊我們要使用的是主選單中的Project選單中的『Build』這個選項。當我們Build過程如果沒有任何錯誤的話,就會跳出一個對話窗要我們按下OK確定編譯完成。
14
29-1. DLL動態函式庫開發 範例29-1:DLL動態函式庫開發(BCB Style DLL) Step 4: 編譯dll (2)
當我們Build完成之後,可以去看看該專案的目錄底下是不是已經有一個lib和一個dll的檔案,如果沒有,將前面這四個步驟再重複看一次,看看自己是不是在哪個步驟做錯了!
15
29-1. DLL動態函式庫開發 範例29-1:主要程式碼(main.cpp) #pragma argsused
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved) { return 1; } // long __declspec(dllexport) __stdcall BCBType_Count(int m, int n) long sum = 0; for (m ; m <= n ; m++) sum += m; return sum;
16
29-1. DLL動態函式庫開發 範例29-2:DLL動態函式庫開發(VC++ Style DLL) 範例說明
17
29-1. DLL動態函式庫開發 範例29-2:主要程式碼(main.cpp) #pragma argsused
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, LPVOID lpvReserved) { return 1; } // long __declspec(dllexport) __stdcall VCType_Count(int m, int n) long sum = 0; for (m ; m <= n ; m++) sum += m; return sum;
18
29-1. DLL動態函式庫開發 範例29-2:DLL動態函式庫開發(VC++ Style DLL)
範例29-1以及範例29-2這兩段程式就是在entry point不一樣。BCB使用的是DLLEntryPoint這種格式,而Visual C++使用的就是DLLMain這樣的格式。 int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved) { return 1; } BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, LPVOID lpvReserved) { return 1; }
19
29-1. DLL動態函式庫開發 範例29-3:DLL函式庫應用 範例說明 在這個範例中,我們會使用到我們剛剛才開發出來的dll。
Step 1: 建立新專案 Step 2: 複製lib以及dll檔案 將範例29-1以及29-2都各產生一個lib以及dll檔案吧!在這邊我們要將這四個檔案全部複製到與範例29-3的同一個目錄下。
20
29-1. DLL動態函式庫開發 範例29-3:DLL函式庫應用 Step 3: 製作一個header file
接下來我們需要替這兩個dll內的函式產生header file,這樣我們才有辦法使用這些包在dll檔案內的函式,我們將這些函式宣告都放在一個叫做user.h的檔案內。產生的方法也是點一下『New』這個按鈕,然後選擇Header File這個選項來新增。 至於這邊要將這個header file取成什麼名字,各位讀者可以自行決定,不一定要使用user.h這樣的檔名,只要在include的時候確定沒有打錯檔名就好。 user.h內容 int __declspec(dllexport) __stdcall BCBType_Count(int m, int n); int __declspec(dllexport) __stdcall VCType_Count(int m, int n);
21
29-1. DLL動態函式庫開發 範例29-3:DLL函式庫應用 Step 4: 將library加入到專案內
接下來這一個步驟可說是最關鍵的步驟,如果沒有將library加入專案內,就算怎麼寫程式,怎麼改程式,在linking的時候一定會出現Linking Error。 要加入library的方法很簡單,我們可以直接按下『Shift + F11』或是利用Project這個選單內的Add to Project的選項來完成。
22
29-1. DLL動態函式庫開發 範例29-3:DLL函式庫應用 Step 5: 撰寫主要程式碼 Step 6: 編譯並且執行程式
23
29-1. DLL動態函式庫開發 範例29-3:主要程式碼(main.cpp) #include "user.h"
// __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } void __fastcall TForm1::Button1Click(TObject *Sender) { int m, n; m = Edit1->Text.ToInt(); n = Edit2->Text.ToInt(); Edit5->Text = BCBType_Count(m, n); void __fastcall TForm1::Button2Click(TObject *Sender) { m = Edit3->Text.ToInt(); n = Edit4->Text.ToInt(); Edit6->Text = BCBType_Count(m, n);
24
29-1. DLL動態函式庫開發 範例29-3:DLL函式庫應用 執行結果
25
29-2. LIB靜態函式庫開發 剛才談完了DLL這種動態函式庫的開發,現在我們要在講另外一種函式庫的開發,那就是LIB – 靜態函式庫。發展靜態函式庫的模式跟發展DLL函式庫很相近,我們將從範例29-4以及範例29-5來說明靜態函式庫的開發過程跟使用方法。
26
29-2. LIB靜態函式庫開發 範例29-4:Lib靜態函式庫的開發 範例說明
在範例29-4中,我們也是要開發一個可以從m加到n的一個函式庫,只是在這個範例中我們不是將這個函式庫開發成dll的格式,而是開發成lib的格式。 Step 1: 開一個新的lib專案 當我們將這個新的專案開啟之後,BCB會自動幫我們將一些相關的程式碼先建立出來。 // #include <vcl.h> #pragma hdrstop #define Library // To add a file to the library use the Project menu 'Add to Project'.
27
29-2. LIB靜態函式庫開發 範例29-4:Lib靜態函式庫的開發 Step 2:建立新的Unit
從BCB自動產生的內容就可以看的出來,我們需要自己新增撰寫程式碼的檔案,也就是得自己生出『.cpp』和『.h』這兩個檔案,既然這兩種檔案都需要產生,我們可以直接利用新增一個『Unit』來解決。 如果不是利用BCB內『New』的方法來新增這個cpp檔案,要記得從Add to Project這個對話窗中把該cpp檔案加入,否則會出現錯誤喔!
28
29-2. LIB靜態函式庫開發 範例29-4:Lib靜態函式庫的開發 Step 3: 撰寫主要程式碼
以上三個步驟都完成後,不要忘記存檔一下,免的辛苦的心血一不小心就不見了。 Step 4: 編譯該Library 接下來我們要做的動作就是Build這個專案,方法跟我們製作dll的時候一樣,利用Project選單中的Build這個選項來Build範例29-4。當BCB出現Build成功的確認對話窗時,也就表示我們的開發過程已經全部完成,這時候各位讀者可以趕快去資料夾內看看,是不是有一個副檔名為lib的檔案被創造出來了!
29
29-2. LIB靜態函式庫開發 範例29-4:主要程式碼(main.h)
// #ifndef mainH #define mainH long LIB_Count(int m, int n); #endif
30
29-2. LIB靜態函式庫開發 範例29-4:主要程式碼(main.cpp)
// #pragma hdrstop #include <stdio.h> #include "main.h" #pragma package(smart_init) long LIB_Count(int m, int n) { int sum = 0; for (m ; m <= n ; m++) sum += m; return sum; }
31
29-2. LIB靜態函式庫開發 範例29-5:Lib靜態函式庫的應用 範例說明
範例29-5我們將利用範例29-1、29-2、以及29-4這三個不同的library來實作出一個程式,雖然這三個library功能都一樣,但是卻有著不同的開發方式,希望能藉著這個範例讓讀者更清楚我們這一章的宗旨。 在範例29-5的實作過程中,其實跟範例29-3幾乎一模一樣,所以我們就不在一步一步的慢慢講解,直接列出程式碼和結果出來,如果對於範例29-5的實作過程中有不清楚的,只要往回看一下範例29-3的步驟,把要Import的Library從原本的兩個再增加一個,user.h改一改,這樣就差不多完成了。
32
29-2. LIB靜態函式庫開發 範例29-5:主要程式碼(user.h)
int __declspec(dllexport) __stdcall BCBType_Count(int m, int n); int __declspec(dllexport) __stdcall VCType_Count(int m, int n); long LIB_Count(int s, int e);
33
29-2. LIB靜態函式庫開發 範例29-5:主要程式碼(main.cpp) #include "user.h"
// void __fastcall TForm1::Button1Click(TObject *Sender) { int m, n; m = Edit1->Text.ToInt(); n = Edit2->Text.ToInt(); Edit5->Text = BCBType_Count(m, n); } void __fastcall TForm1::Button2Click(TObject *Sender) { m = Edit3->Text.ToInt(); n = Edit4->Text.ToInt(); Edit6->Text = BCBType_Count(m, n); void __fastcall TForm1::Button3Click(TObject *Sender) { m = Edit7->Text.ToInt(); n = Edit8->Text.ToInt(); Edit9->Text = LIB_Count(m, n);
34
29-2. LIB靜態函式庫開發 範例29-5:Lib靜態函式庫的應用 執行結果
35
本章習題 設計一個應用程式使用動態Library。 設計一個應用程式使用靜態Library。
Similar presentations