2010-08-09 27 views
7

如果你在Delphi內部找到足夠的東西,你會發現一些奇怪的東西,並且顯然沒有關於編譯器生成的TTypeInfo記錄的文檔。如果PTypeInfo點地址X一TTypeInfo記錄,在X - 4你會發現在接下來的4個字節描述一個指向X.例如:什麼是TTypeInfo之前的「標識指針」?

procedure test(info: PTypeInfo); 
var 
    addr: cardinal; 
    ptr: PPointer; 
begin 
    addr := cardinal(info); 
    writeln('addr: ', addr); 
    dec(addr, 4); 
    ptr := PPointer(addr); 
    addr := cardinal(ptr^); 
    writeln('addr: ', addr); 
end; 

傳遞由編譯器生成的任何合法PTypeInfo進入這個程序,它會輸出兩次相同的地址。我在TypInfo.pas中稍微探討了一下,但是我沒有看到任何提及這個「身份指針」或它的用途。有誰知道這是爲什麼嗎?從德爾福的至少D3到D2010,這在每個版本的Delphi中都是如此。

+1

我假設在上面的代碼中缺少'var' ... – 2010-08-09 19:45:34

+0

@Andreas:哪裏?我沒有看到任何遺漏的變種... – 2010-08-09 19:50:34

+0

哎呀!好,現在就修好了。 – 2010-08-09 20:00:57

回答

10

這很簡單:包和動態鏈接。

BPL是DLL。通過修補表連接DLL,而不是EXE中的所有代碼或鏈接到修補DLL的DLL鏈接(這會對多個進程之間的只讀內存共享造成很大的傷害)。爲了防止在代碼的某處引用TypeInfo(SomeType),或者在鏈接到BPL時修改EXE或DLL的typeinfo,而不是通過導入表進行間接引用。

可以很容易地看到與此計劃針對BPL鏈接靜態鏈接時的區別:

{$apptype console} 
uses TypInfo, SysUtils; 
type 
    TFoo = class(TObject); 
var 
    x: PPTypeInfo; 
begin 
    x := GetTypeData(TypeInfo(TFoo))^.ParentInfo; 
    Writeln(x^^.Name); 
    Writeln(Format('x %p', [x])); 
    Writeln(Format('x^ %p', [x^])); 
end. 

在我的本地機器上,用dcc32 test.pas編譯,它輸出:

TObject 
x 00401B64 
x^ 00401B68 

但是,當編譯與dcc32 -LUrtl test.pas的RTL包,它輸出:

TObject 
x 004051F0 
x^ 40001DA4 

希望這可以清除它。

+0

)我只是要制定一個非常相似的想法。感謝您的洞察力。 – 2010-08-10 06:37:08

+0

因此,指針必須位於鏈接器完成工作的某個位置,並且恰好位於它指向的數據之前,當這些數據在同一個模塊中時,按照慣例?OK,這是有道理的。謝謝 – 2010-08-10 11:54:01

+2

@Mason所有類型信息修正 - 從一個類型信息blob到另一個類型的指針 - 類型是PPTypeInfo,而不是PTypeInfo,用於處理動態鏈接的情況。在靜態鏈接的情況下,需要一箇中間指針來使得約定工作,並且typeinfo本身的一部分具有與任何其他的一樣的意義。也就是說,鏈接器不在那裏;它是因爲約定和約定在那裏,因爲動態鏈接,這是儘可能擴大頁面共享的潛力。 – 2010-08-10 13:58:33

0

也許它的鏈接列表恰好是在連續的內存:)

+0

不,因爲TTypeInfo/TTypeData結構中沒有任何類似「下一個指針」的東西。雖然有趣的想法。 – 2010-08-09 19:18:20

1

不要完全明白是怎麼回事,但是當你在TypInfo單位看看例如IsPublishedProp,你會看到它蒙上了實例的ClassInfo作爲一個指向所屬類別結構:

PTypeInfo(Instance.ClassInfo) 

當你看的ClassInfo方法,它返回一個簡單的指針,它的價值似乎相關VMT表:

Result := PPointer(Integer(Self) + vmtTypeInfo)^; 

vmtTypeInfo的值爲-72。在-76之前的四個字節是vmtInitTable。 vmtTypeInfo後面跟着FieldTable,MethodTable,DynamicTable等。

vmtInitTable的值用於例如TObject.CleanupInstance並作爲指向TypeInfo結構的指針傳遞給_FinalizeRecord

因此,指向TypeInfo結構的TypeInfo結構之前的四個字節似乎是由設計和部分vmt結構存在的。

編輯

梅森正確地指出上面是一個完整的紅鯡魚(見註釋)。我正在離開答案,以便其他人不必追究。

更新 爲了避免過度變量及其地址混淆,我重寫了梅森的測試程序如下:

procedure test(info: PTypeInfo); 
begin 
    writeln('value of info  : ', cardinal(info)); 
    writeln('info - 4   : ', cardinal(info) - 4); 
    writeln('value 4 bytes before: ', cardinal(PPointer(cardinal(info)-4)^)); 
end; 

,並提供下列信息調用它:

procedure TryRTTIStuff; 
begin 
    writeln('TPersistent'); 
    test(TypeInfo(TPersistent)); 

    writeln('TTypeKind enumeration'); 
    test(TypeInfo(TTypeKind)); 

    writeln('Integer'); 
    test(TypeInfo(Integer)); 

    writeln('Nonsense'); 
    test(PTypeInfo($420000)); 
end; 

第一三個產生了梅森描述的結果。我只添加了一個額外的記錄來顯示最後一個記錄的指針值。 TryRTTIStuff中的最後一個調用是爲了表明,當您沒有傳入指向有效TypeInfo結構的指針時,您不會在第一個和第三個writeln的調用中獲得相同的值。

還沒有線索尚未與TypeInfo進行。也許我們應該問巴里凱利,因爲他是新的D2010 RTTI的作者,所以應該知道很多關於舊的...

+0

對不起,但你看錯了。 TypeInfo與VMT無關,因爲它也可以用於非對象類型。 VMT包含一個指向類的TTypeInfo記錄的指針,但這不是我所問的。 – 2010-08-09 19:49:08

+0

@Mason:你可能是完全正確的,但我認爲「舊式RTTI」只支持TypeInfo的類和枚舉?只有在枚舉聲明中沒有指定特定值時才使用後者。其他類型的RTTI在D2010之前並沒有出現,我認爲(我正在使用D2009)。 – 2010-08-09 20:12:03

+0

編號類型信息必須可用於任何可用於DFM的基本類型,因爲這是系統發明的原因:表單序列化和反序列化。 – 2010-08-09 20:21:45