RegisterClass

在 2002 年 8 月我在 Programer 深度論壇中,發表了一篇關於 RegisterClass 的文章,當時是依照記憶中的印象回答網友的問題,但是沒多久就發現文章內容可能有問題,卻因為時間的因素,我無法證實文章可疑之處是否有誤,因此撰寫此文加以釐清整個 RegisterClass 的用法。希望對這方面有興趣的人有所幫助。
RegisterClass 方法依照 Delphi v6.0 Online Help 的解釋並不清楚,只是說將 Class 的資訊利用 streaming system 來註冊的,但是並未說明註冊到何處?然而根據我刨根究底的追查發現,其實 RegisterClass 是將 Class 的資訊註冊到 RegGroups 這個 Delphi 預設物件當中的(而這個物件在程式碼中無法存取),而 RegGroups 這個物件類別的宣告是這樣子的
 
 TRegGroups = class
  private
    FGroups: TList;
    FLock: TRTLCriticalSection;
    FActiveClass: TPersistentClass;
    function FindGroup(AClass: TPersistentClass): TRegGroup;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Activate(AClass: TPersistentClass);
    procedure AddClass(ID: Integer; AClass: TPersistentClass);
    function GetClass(const AClassName: string): TPersistentClass;
    function GroupedWith(AClass: TPersistentClass): TPersistentClass;
    procedure GroupWith(AClass, AGroupClass: TPersistentClass);
    procedure Lock;
    procedure RegisterClass(AClass: TPersistentClass);
    procedure RegisterClassAlias(AClass: TPersistentClass; const Alias: string);
    function Registered(AClass: TPersistentClass): Boolean;
    procedure StartGroup(AClass: TPersistentClass);
    procedure Unlock;
    procedure UnregisterClass(AClass: TPersistentClass);
    procedure UnregisterModuleClasses(Module: HMODULE);
    property ActiveClass: TPersistentClass read FActiveClass;
  end;
 
這個對於我當初說 RegisterClass 會將資訊註冊到 Windows 的 Registry 當中的說法,有相當大的出入,不過 Delphi 之前版本在 RegisterClass 的做法上,是否真如我所說的,我已無法可考證(因為我早已沒有 Delphi v2.0 以前的 Source Code 可以追蹤),不過我們卻可以針對 Delphi v6.0 以後的版本來研究一下,Delphi 到底是如何運作 Class 的資訊。
根據 Online Help 的說明,經表單所宣告要參考到的 Form 類別及 Component 類別,我們可以當作已經自動註冊過的東西。所以我們不必擔心執行時期,程式會參考不到類別的定義,因此我們用下面的例子來說明一下:
先開啟一個新的專案,並且在專案中加入兩個 Form1 及 Form2,然後在 Form1中寫下以下的程式碼:
implementation
uses Unit2;
 
{$R *.dfm}
 
procedure TFmMain.Button1Click(Sender: TObject);
begin
   RegisterClass(TForm2);
end;
 
procedure TFmMain.Button2Click(Sender: TObject);
begin
   if FindClass('TForm2') <> nil then
      ShowMessage('Found !!')
end;
 
我們發現必須在按下 Button1 註冊 TForm2 之後,再按下 Button2 程式才能找到 Form2 的定義,這個與 Online Help 說明是相符的。現在我們在 Form2 中以 TImage 放入一張很大的圖檔,然後在 dpr 檔案中移除 Unit2 單元,而後再試一次,我們發現依然可以成功。但是檢查執行檔的大小,卻發現在 dpr 專案檔移除Unit2 的前後並沒有改變,但是如果一併移除 uses 對於 Unit2 的宣告,並且 Mark 掉 RegisterClass(TForm2);  這行指令,則可以發現執行檔的大小明顯縮小了,但是執行的結果卻失敗了。這說明了 Delphi 的編譯器會因為類別參考的需要,而將類別定義的資訊,全部編譯到執行檔中。而 RegisterClass 則是將類別資訊另行註冊到 RegGroups 的物件當中罷了。
現在我們再回頭看看 RegGroups 的定義。在 RegGroups 定義中有一個 FGroups 的成員,它的型態是 TList。然後再看一下 AddClass 的成員函數
 
procedure TRegGroups.AddClass(ID: Integer; AClass: TPersistentClass);
begin
  TRegGroup(FGroups[ID]).AddClass(AClass);
end;
 
赫赫!!果然我們證明了呼叫 RegisterClass 只是另外複製一份類別資訊到 Delphi 預設的 RegGroups 當中,與執行檔中已存的類別定義並沒有關係,只是當程式結束時,我們在執行時期用 RegisterClass 所註冊的類別資訊會一併消失而已。這個與我在 Programmer 深度論壇中所說 ”使用 RegisterClass 必須保證類別定義讓程式能夠找到” 的這句話有一些出入。不過沒關係,雖然記憶這個東西,有的時候會不太可靠,但是只要凡事多驗證一下,還是可以得到正確的結果。所以到目前為止,我們可以做一個小小的結論,是在 Delphi v6 中使用 RegisterClass 並不會造成什麼樣的不良的結果。
回歸正題,從上面測試的內容中,我們可以發現一點,那就是類別的定義資料必須編譯在執行的程式碼中,才可以正確執行。這樣一來使用者在用 RegisterClass 迂迴的建立物件的方式,除了一些特殊的目的之外,似乎有一點多餘。所以如果能夠不把類別定義編譯到程式碼中,然後也能正確的建立物件,那豈不是能夠顯得出真正的價值。於是我們再回到 Source Code 中追蹤看看。我記得 TStream 類別中有一個方法是 ReadComponent,它的原型是這樣子的
var
  Reader: TReader;
begin
  Reader := TReader.Create(Self, 4096);
  try
    Result := Reader.ReadRootComponent(Instance);
  finally
    Reader.Free;
  end;
end;
 
然後 TReader 的 ReadRootComponent 原型則又是這樣子的
 
……
try
      ReadPrefix(Flags, I);
      if Root = nil then
      begin
        Result := TComponentClass(FindClass(ReadStr)).Create(nil);
        Result.Name := ReadStr;
      end else
      begin
……
end;
except
end;
……
 
我們可以發現在上面的程式段落中的 TComponentClass(FindClass(ReadStr)).Create(nil); 這行指令,其實就是在找RegGroups 物件中,是否有相對應的類別定義,所以我推論如果 RegGroups 不存在應有的類別定義,那麼 TStream.ReadComponent 的呼叫肯定會發生錯誤,事實上,經過我的實驗之後結果也的確如此。換句話說,由於 RegGroups 是存在於執行檔中,所以物件的類別定義我們可以推論是無法存在執行檔以外的地方。因此我們再退一萬步來看,當初我追蹤 RegisterClass 的目的,在於是否能夠將類別定義從執行檔中拆解出來,但是如果 Delphi 所編譯出來的執行檔,可以允許執行本身以外的類別定義,那麼豈不就是跟 Java 等具有直譯器功能的平台一樣了嗎??嗯!!基本上 Delphi 編譯出來的東西應該沒那麼偉大!!所以這個議題追蹤到這裡應該就可以停止了。因為當初希望類別定義的目的在於分割應用程式,但是分割應用程式有許多種方法,實在犯不著用這麼迂迴的方式!!
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值