2010-08-09 4 views
7

Wenn Sie in den Delphi-Interna genug herumstöbern, werden Sie etwas befremdlich und nicht dokumentiert über TTypeInfo-Datensätze finden, die vom Compiler generiert wurden. Wenn die PTypeInfo Punkte auf einen TTypeInfo Rekord bei der Adresse X, bei X - 4 Sie die nächsten 4 Bytes finden einen Zeiger auf X. Zum Beispiel beschreiben:Wofür steht der "Identity Pointer" vor einer 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; 

Pass jede legitime PTypeInfo durch den Compiler in diese Routine erzeugt, und es wird die gleiche Adresse zweimal ausgeben. Ich habe in TypInfo.pas ein bisschen herum gestöbert, aber ich sehe nichts, was diesen "Identitätszeiger" erwähnt oder wofür er da ist. Weiß jemand, warum das da ist? Dies scheint in jeder Version von Delphi von mindestens D3 bis D2010 zu gelten.

+1

Ich nehme an, eine 'var' fehlt im obigen Code ... –

+0

@Andreas: Wo? Ich sehe keine fehlenden vars ... –

+0

Ups! OK, jetzt behoben. –

Antwort

10

Es ist sehr einfach: Pakete und dynamische Verknüpfung.

BPLs sind DLLs. DLLs werden durch Tabellen verknüpft, die gepatcht werden, anstatt dass der gesamte Code in der EXE oder der DLL mit der DLL verknüpft wird, die gepatcht wird (was der gemeinsamen Nutzung von Nur-Lese-Speicher zwischen mehreren Prozessen großen Schaden zufügen würde). Um zu verhindern, dass ein Verweis auf TypeInfo(SomeType) irgendwo im Code oder typeinfo einer EXE oder DLL benötigt wird, die bei der Verknüpfung mit der BPL geändert werden, gibt es stattdessen eine Indirektion durch die Importtabelle.

Es ist einfach, den Unterschied zu sehen, wenn im Vergleich zu Verknüpfung mit einem BPL in diesem Programm statisch verknüpfen:

{$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. 

auf meinem lokalen Rechner, mit dcc32 test.pas kompilierte, gibt sie:

TObject 
x 00401B64 
x^ 00401B68 

Aber wenn kompiliert mit dem RTL-Paket mit dcc32 -LUrtl test.pas, gibt es aus:

TObject 
x 004051F0 
x^ 40001DA4 

Hoffentlich klärt es auf.

+0

Ich wollte nur eine ganz ähnliche Idee formulieren. Danke für die Erkenntnis. –

+0

Also muss der Zeiger irgendwo sein, damit der Linker seinen Job macht, und er wird zufällig direkt vor den Daten platziert, auf die er zeigt. wenn diese Daten im selben Modul sind, nach Vereinbarung? OK, das macht Sinn. Danke. –

+2

@Mason alle Typeinfo-Korrekturen - Zeiger von einem Blob von Typinfo zu einem anderen - sind vom Typ PPTypeInfo, nicht PTypeInfo, um die dynamische Verknüpfung Fall zu behandeln Im Fall der statischen Verknüpfung muss es einen Zwischenzeiger geben, damit die Konvention funktioniert, und ein Teil von typeinfo selbst macht so viel Sinn wie irgendjemand, das heißt, es ist nicht da für den Linker, er ist da wegen Die Konvention, und die Konvention ist da wegen der dynamischen Verknüpfung, die so gemacht wird, um das Potenzial für die gemeinsame Nutzung von Seiten zu maximieren. –

0

vielleicht seine eine verknüpfte Liste, die in zusammenhängenden Speicher passiert sein :)

+0

Nein, weil in den TTypeInfo/TTypeData-Strukturen nichts dem "nächsten Zeiger" ähnelt. Interessante Idee. –

1

Verstehe nicht ganz, was los ist, aber wenn man einen Blick auf zum Beispiel IsPublishedProp in der TypInfo Einheit haben, werden Sie sehen, dass es den Class der Instanz als Zeiger auf eine Typeinfo Struktur wirft:

PTypeInfo(Instance.ClassInfo) 

Wenn Sie die Class Methode zu buchen, es gibt einen einfachen Zeiger, dessen Wert auf die vmt Tabelle, scheint:

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

vmtTypeInfo hat einen Wert von -72. Vier Bytes davor bei -76 ist vmtInitTable. vmtTypeInfo wird gefolgt von FieldTable, MethodTable, Dynamic usw.

der vmtInitTable Wert verwendet wird zum Beispiel in TObject.CleanupInstance und weitergegeben _FinalizeRecord als der Zeiger auf die Typeinfo-Struktur.

So scheinen die vier Bytes vor der TypeInfo-Struktur, die auf die TypeInfo-Struktur verweist, von Entwurf und Teil der vmt-Struktur zu sein.

bearbeiten

Als Mason zu Recht des Obige ein vollständiges Ablenkungsmanöver darauf hingewiesen (siehe Kommentar). Ich verlasse die Antwort, damit andere sie nicht verfolgen müssen.

aktualisieren Um Verwirrung über Variablen und deren Adressen zu vermeiden, habe ich Mason Testverfahren wie folgt neu gefasst:

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; 

und nennen Sie es mit den folgenden Informationen:

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; 

der ersten drei produzieren die Ergebnisse, die Mason beschreibt. Ich fügte nur einen zusätzlichen writelnn hinzu, um den Zeigerwert für das letzte writeln anzuzeigen. Der letzte Aufruf in TryRTTIStuff soll zeigen, dass Sie, wenn Sie keinen Zeiger auf eine gültige TypeInfo-Struktur übergeben, nicht den gleichen Wert für den ersten und dritten Schreibvorgang für den Aufruf erhalten.

Noch keine Ahnung, was mit der TypeInfo passiert. Vielleicht sollten wir Barry Kelly fragen, da er der Autor des neuen D2010 RTTI ist und daher viel über den alten wissen sollte ...

+0

Sorry, aber du schaust auf die falsche Sache. TypeInfo ist nicht mit VMTs verknüpft, da es auch für Nicht-Objekttypen existieren kann. Die VMT enthält einen Zeiger auf den TTypeInfo-Datensatz der Klasse, aber darum geht es mir nicht. –

+0

@Mason: Sie haben vielleicht vollkommen Recht, aber ich dachte, dass "alten Stil RTTI" nur TypeInfo auf Klassen und Aufzählungen unterstützt? Und Letzteres nur, wenn Sie in der Aufzählungserklärung keine bestimmten Werte zuweisen. RTTI für andere Typen erschien erst ab D2010 (ich benutze D2009). –

+0

Nein. Typinformationen mussten für jeden Basistyp verfügbar sein, der in einem DFM verwendet werden könnte, da das System dafür erfunden wurde: Formularserialisierung und Deserialisierung. –