小技巧:Delphi對project group的編譯是按照列表順序從上到下進行的,因 此在有些時候,被require或者use的文件如果在下面,那麼可能會提示找不到文 件。因此最好用文本編輯器調整一下bpg文件中的列表順序。
現在我們看到,package工程的入口dpr文件結構中沒有uses子句,但是取而 代之的是contains子句。從字面上說,這好像是指明這個包將會由哪些Unit組成 。這些Unit再去use別的unit,這樣就又形成了一張有向圖。是這樣的嗎?我們 將Package1的contain部分改成
contains
UnitForm1 in 'UnitForm1.pas' {Form1};
這樣僅包含UnitForm1。而UnitForm1中因為存在繼承關系,必然要use UnitFormAnother。於是自然package1中必須包含三個Unit:Package1.dpk、 UnitFormAnother.pas、UnitForm1.pas。然後編譯,結果報警說:隱式地引入了 UnitFormAnother。先不管這個警告,然後改exe工程:把FormMain的 button1Click改成
procedure TFormMain.Button1Click(Sender: TObject);
var
LForm:TForm1;
begin
LForm:=TForm1.Create(Application);
LForm.ShowModal;
LForm.Free;
end;
當然,還要在FormMain的Uses裡面加上UnitForm1,然後修改runtime package列表,加上package1。然後編譯,調試,檢查Module情況。結果發現 ProjectExe裡面包含FormMain,而Package1.pbl裡面包含我們所推測的三個Unit 。另一個試驗是,如果在編譯exe的時候,去掉列表中的package1,並且恰好能 讓編譯器找到FormAnother和Form1(源文件也好,dcu也好),也可以成功編譯 。但是此時三個Form都跑到exe中間去了。
類似地,驗證package2,發現:package2也可以編譯過,前提是它能夠找到 FormAnother,此時package2中包含兩個Form;如果把package2的require部分改 成只依賴package1,那麼最終編譯出來的package2中則只含Form2。如果兩個 package都包含FormAnother,而exe同時使用兩個package的話,那麼會產生編譯 錯誤。(是啊,兩個package同時加載一個類,當我要使用的時候,到底是誰提 供服務呢?)這種情況很容易發生,因為一方面FormAnother是Form1和Form2的 公共基類;另一方面,在Contain子句裡面很容易不小心漏掉FormAnother。所以 編譯器的提示還是很不錯的,寫程序還是按規矩辦事,把contain寫完整比較好 。
因此,現在基本可以得到結論:
由dpr文件的runtime library(或者dpk文件的requires子句)出發,再繼續 搜索這些bpl require的bpl,直到獲得所需的所有bpl文件。設這些bpl中包含的 Unit組成集合A。然後從dpr文件的uses子句(或dpk文件的contains子句)出發 ,生成工程所需Unit的集合B。則最後編譯目標僅包含B-A。
集合B中的Unit,如果存在於集合A中,則在連接時不需要對應的dcu文件,有 bpl和bpc就好了。而B-A部分,要麼必須有pas文件,要麼需要有dcu文件。
現在,編譯和連接的問題基本解決了,現在來研究加載。加載有兩種,一種 是自動的,由Delphi控制;一種是手動的,在程序中寫LoadPackage。先來搞清 楚什麼情況下會自動加載library。
測試是這樣的,ProjectExe use UnitForm1, Package2 contains UnitForm2 requires package1,package1 contains UnitForm1和 UnitFormAnother。在ProjectExe的Package list裡面僅有package2。運行結果 是:加載的包有rtl、vcl和package1,package2並沒有出現。也就是說,自動裝 入內存的包是那些存在於A集合中,且跟B集合有交集的包。所有想要完全手工加 載包,還必須要注意一些問題,起碼它不能直接和間接地被require,包中的 Unit也不能在Use裡面出現。換句話說,調用者完全不知道被調用包的情況下才 能避免自動裝載。
既然調用者完全不知道被調用的包的信息,憑什麼去調用呢?Delphi裡面似 乎沒有頭文件之類的東西。怎麼獲取這個包的接口呢?