BPL vs. DLL (原文http://www.delphi3000.com/ 翻譯:房客) 第一部分:有關包的介紹 一般我們編寫編譯一個DELPHI應用程式時,會產生一個EXE檔案,也就是一個獨立的WINDOWS應用程式。很重要的一點:區別於Visual Basic,DELPHI產生的是預先包裹的應用程式是不需要大量的運行庫(DLL's)。
假設:開啟Delphi預設的工程(只有一個空白form),F9她將編譯產生一個大約295 KB (Delphi 5)的可執行檔。然後開啟Project | Options,把‘Build with runtime packages’選上再編譯一下,EXE檔案大小就只有15 KB左右了。 我們編譯一個DELPHI應用程式時預設地沒有選擇'Build with runtime packages',編譯器將把程式運行所需要的代碼直接寫入你的EXE檔案中,因此產生的程式是一個相對獨立的程式,並不需要任何附屬的支援檔案(例如動態運行庫檔案DLL),這也就知道了為什麼DELPHI產生的應用程式為什麼都那麼大。 要建立儘可能小的DELPHI程式,方法之一就要充分發揮Borland package libraries的作用,簡稱BPL。 先說什麼是包? 簡而言之,一個包就是一個在DELPHI的IDE環境中被DELPHI應用程式共用的特殊的動態連結程式庫。包允許我們通過多級應用將我們的程式的一部分當做一個分離的模組供其他應用程式來共用。 包大致可分為運行期包(Run-time packages)和設計期包(Design-time packages): 運行期包-當運行程式時提供VCL和庫函數的支援,操作上很類似標準的動態連結程式庫。 設計期包-用來在DELPHI的IDE環境安裝控制項和為控制項建立特殊的屬性編輯器。設計期包允許包含控制項、屬性和控制項編輯器等等,在IDE環境中,這類包是程式設計所必需的,也僅僅是DELPHI使用,並不和開發的應用程式一起分發。 知道這些包的運用,我們也就知道了運行期包是如何做處理和它們對DELPHI程式員有什麼協助了。 有一點必須說明:想很好地運用包並不要求你先成為一個成熟的控制項編寫者。DELPHI編程初學者也可以和應該嘗試去接觸包的概念,這將有利於你更好地理解包和DELPHI的工作關係。 第二部分:適時運用包裹和DLL
一般都認為加入WINDOWS作業系統中的動態運行庫是一種最有用最高效的應用。在WINDOWS系統中,很多應用程式同時運行可能會引起了記憶體方面的問題,很多程式執行相似的操作任務,但各自又由不同的代碼來控制並完成任務,動態運行庫的作用就是將你的執行程式中的這些代碼放到一個系統共用環境下的DLL中去。可能最為直觀的動態連結程式庫例子就是WINDOWS作業系統自己和它本身所帶的API了。 動態連結程式庫通常都是用來集合過程(procedure)和函數(function)以供程式調用。當然我們在編寫動態連結程式庫的同時,也可以把一個DELPHI FORM放到一個DLL中去(例如一個AboutBox FORM),此外我們也可以在DLL中儲存程式所需要的資源(resources)。更多關於DELPHI如何操作使用動態連結程式庫,請參考相關書籍,不再贅述。 在比較DLLs 和BPLs之前,我們先要知道可執行檔的2種代碼連結的方式:靜態連結和動態連結。 靜態連結就是當一個DELPHI工程被編譯的時候,工程所需要的所有代碼將被直接連結入你的程式執行檔案。結果就是執行檔案將包含程式所需要使用到的所有單元(units),你也許會說這樣代碼有點冗長,因為在通常預設情況下,一個FORM單元的uses子句列舉了至少5個基本單元(如:Windows, Messages, SysUtils,...),儘管如此,DELPHI還是能夠智能地自動連結單元中真正要用到的代碼到工程代碼中,從而儘可能地減少了執行檔案的大小。使用靜態連結,我們的應用程式就是一個相對獨立的程式,不需要任何額外的支援檔案或動態連結程式庫(暫時不考慮BDE和ActiveX構件)。DELPHI中預設使用的就是靜態連結方式。 動態連結就是應用程式將和標準的動態連結程式庫(DLLs)一起運行。動態連結方式不需要將代碼直接建立到每個應用程式中去,單獨為多個應用程式提供多線程的庫函數支援,任何程式運行期間才需用到的包才將被載入,更值得一提的是:程式在動態方式需要調用的包是自動載入的,因此你不需要專門寫載入包的代碼。 方法:簡單地選中在Project | Options 對話方塊中'Build with runtime packages'複選框後,再次編譯你的程式,你的程式碼將自動連結到動態運行包,而不是將引用單元都靜態連結入你的工程執行檔案。 是選擇BPL 還是 DLL?區別又在哪裡? 你可能很奇怪為什麼要選擇使用運行期包,而不是DLL,或者還有其他什麼方法。 相對於DLL而言,包裹的概念是DELPHI開發中所特有的,就是說其他語言編寫的應用程式不能引用DELPHI建立的包裹。即使包是一種被DELPHI編寫的應用程式所使用的動態連結程式庫,它也同時提供給了DELPHI程式員更多的庫函數支援。 通常我們在DELPHI中建立動態連結程式庫(DLLs)是用來儲存不同環境下應用程式所需要使用到的過程和函數,而包不僅能夠包含代碼單元(untits)、構件和FORMs,還能包含DELPHI中的類(classes)-這就使我們能夠在其中引用對象嚮導編碼(object oriented code)。在包裹中,我們可以儲存完整的通用DELPHI構件,而動態運行庫(DLL's)對此則無能為力了。 此外,在縮減程式碼上,DLLs和BPLs扮演著同樣重要的角色,其主要原因就是在使用包裹或動態連結程式庫技術後,都直接地減少了程式的檔案大小。當然,還要說明的是:執行程式需要載入的DLLs或BPLs也可能會是很龐大的。例如如果需要分發你的包裹檔案(主要是VCL包,vcl50.BPL)至少有2MB左右。 儘管如此,如果你是要分發共用同個包的多個應用程式,你就可以省很多事了。當使用者方系統中已經存在程式運行需要的部分檔案(如:標準的DELPHI BPLs)後,就只需要下載程式的最小執行檔案了。如果你的程式工程主要是通過INTERNET等方式分發和開展,那效率顯然有很大的提高。 同時,包的應用也節省系統記憶體,因為動態連結的結果就是:只有一個VCL被讀入記憶體供所有使用運行期包的DLEPHI應用程式使用。 包裹的版本問題 當你想升級你的動態連結程式庫時(改變其中一些執行函數),你可以簡單地編譯產生新的程式檔案,並上傳新版本檔案,所有正在使用該動態連結程式庫的應用程式仍將工作(除非你已經將存在的舊版本程式去除)。 換個角度來講,在升級新包裹檔案的同時,不要忘記升級程式的執行檔案。正如你所瞭解的,包裹檔案就是一個單元檔案(units)的集合,所有編譯過的單元檔案(DCU)都含有版本資訊,因此,除非我們有單元檔案的源碼(source),否則我們不能在DELPHI4或5中使用編譯過的單元,所以一旦我們改變了單元檔案中介面部分uses子句中列舉出的任一單元檔案,該檔案就需要重新編譯。編譯器將檢查DCU檔案的版本資訊,並決定單元是否需要重新編譯。因此我們不能在DELPHI5編譯的應用程式中使用在DELPHI6下編譯的包,任何為你的應用程式服務的包和你的應用程式必須在相同環境下編譯。 因此,當給包裹命名的時要保留包裹名中包含有DELPHI的版本資訊(如'AboutDP50',其中50就代表Delphi 5)。這可以有效防止檔案版本的衝突問題,也可以避免很多不必要的麻煩,包使用者可以更清楚包的版本和包裹適用於哪個DELPHI編譯器。 如果你要分發運行期或設計期包給其他DELPHI程式員,建議同時提供了.DCP(含有包的頭資訊和各個單元檔案)和.BPL檔案,還有包中所包含的所有單元檔案的.DCU檔案。 第三部分:建立和使用運行期包 建立一個包裹 建立一個包很簡單,但在建包之前要做一些準備工作。首先,你需要知道你準備建立哪種類型的包檔案:運行期包還是設計期包,或兩者都是;其次是,建立、調試,反覆地測試你想放置到包中去的單元檔案;最後,為建立的包裹檔案命名,還有就是選擇一個合適的地方來存放檔案。 建立一個新的運行期包,按照以下步驟: 1.啟動DELPHI,並選擇File | Close All關閉預設的工程。 2.選擇File | New...,在"New items"對話方塊中的"new"頁面中雙擊Package表徵圖(),就會出現包裹編輯器():包裹編輯器包含2個檔案夾:Contains和Requires。
3.點擊Add按鈕,可以增加一個單元檔案(構件或是一個簡單的代碼單元檔案)。注意:你添加的是PAS源碼檔案而不是編譯後的DCU檔案。當你添加單元檔案的同時,包中的單元的名字就顯示在包裹編輯器的Contains檔案夾中了。中添加了FindFile和PictureClip的單元檔案。 4.開啟Requires檔案夾,展開的列表表示包裹所需要的包的DCP檔案,包裹檔案最基本的就需要引用含有絕大部分標準可視控制項的vcl50.dcp檔案。 5.當你添加完單元檔案,單擊Options按鈕,在Description面板中的Usage options組中你需要選擇包裹種類:是設計期包,還是運行期包,或者兩者都是。如果選擇Runtime only(僅運行期包),其他包的使用者將無法將圖示2個構件安裝到IDE環境中去。 6.使用File | Save儲存包工程檔案(DPK),然後儲存包檔案,如AboutDP50,包裹檔案的命名將很重要。 7.在包裹器中,單擊Compile按鈕來編譯包。 8.如果不出什麼意外,編譯包後將建立一個包裹檔案(BPL檔案),期間你可能還要確定你必須增加的其他包裹(例如VCLX50),這些包都將在Requires檔案夾中列出。 9.完成,Borland package library檔案已經成功建立,就等著使用了。 在包裹編輯器中有一個Install按鈕,就是用來將當前包裹安裝成一個設計期包的。如果包裹是run-time only(僅運行期包),那Install按鈕將無法使用。 關於所建立的這些檔案 除了DPK檔案和那些單元源碼檔案,DELPHI還使用包裹的動態連結版本產生一個BPL檔案和一個含有包內標識資訊DCP檔案,DCP檔案就是包中所包含單元檔案的編譯檔案(DCU)的標識資訊的集合。 使用運行期包設計程式 開始使用動態連結編寫應用程式時,不需要寫代碼去載入運行期包裹,只需要在Project | Options中做相關設定,選擇在Packages頁的Runtime Packages編輯框中的包裹列表,編譯器時將自動連結。應用程式要使用到一個運行期包時,使用ADD按鈕來增加包裹檔案。
注意:儘管一個應用程式被連結到運行期包,程式的USES子句所列出的單元檔案也必須都是存在的,編譯運行期包裹只是告訴應用程式哪裡可以找到構件代碼而已。 當配置一個使用到運行期包裹的應用程式時,確定使用者擁有可執行檔和程式所需的庫檔案(.BPL或.DLL)。如果庫檔案在和EXE檔案不同的目錄下,必須將其指定到系統所能到達的目錄。因此,最好的選擇就是Windows\System系統目錄。 總而言之 包裹使你能夠有彈性地選擇應用程式的分發方式,也使構件的安裝變簡單了,此外,使用包裹也減少了應用程式的檔案大小,因此,使用包裹的開發方式還是具有不小的意義的。 |