8

Delphiの内部を十分に調べてみると、コンパイラによって生成されたTTypeInfoレコードについて、奇妙で明らかに文書化されていないものが見つかります。PTypeInfoがアドレスXのTTypeInfoレコードを指している場合X - 4、次の4バイトはXへのポインターを表します。次に例を示します。

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をこのルーチンに渡すと、同じアドレスが2回出力されます。TypInfo.pasを少し調べましたが、この「IDポインター」またはその目的について言及しているものは何もありません。なぜこれがあるのか​​誰か知っていますか?これは、少なくともD3からD2010までのDelphiのすべてのバージョンに当てはまるようです。

4

3 に答える 3

12

それは非常に単純です:パッケージとダイナミックリンク。

BPLはDLLです。DLLは、パッチが適用されているDLLに対してリンクしているEXEまたはDLLのすべてのコードではなく、パッチが適用されているテーブルを介してリンクされます(これは、複数のプロセス間で読み取り専用メモリを共有することに大きな害を及ぼします)。コード内のどこかへの参照TypeInfo(SomeType)、またはEXEまたはDLLのtypeinfoが、BPLに対してリンクするときに変更される必要をなくすために、代わりにインポートテーブルを介した間接参照があります。

このプログラムで静的にリンクする場合と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

ただし、RTLパッケージを使用してコンパイルするとdcc32 -LUrtl test.pas、次のように出力されます。

TObject
x  004051F0
x^ 40001DA4

うまくいけば、これはそれをクリアします。

于 2010-08-10T06:20:26.750 に答える
1

何が起こっているのかを完全には理解していませんが、たとえばIsPublishedPropユニットTypInfoを見ると、インスタンスのClassInfoがTypeInfo構造体へのポインターとしてキャストされていることがわかります。

PTypeInfo(Instance.ClassInfo)

ClassInfoメソッドを見ると、その値がvmtテーブルに関連していると思われる単純なポインターが返されます。

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

vmtTypeInfo値は-72です。その前の-76の4バイトはvmtInitTableです。vmtTypeInfoの後には、FieldTable、MethodTable、DynamicTableなどが続きます。

たとえば、vmtInitTable値はで使用され、TypeInfo構造体へのポインターとしてTObject.CleanupInstance渡されます。_FinalizeRecord

したがって、TypeInfo構造体を指すTypeInfo構造体の前の4バイトは、設計上、vmt構造体の一部であるように見えます。

編集

メイソンが正しく指摘したように、上記は完全な赤いニシンです(コメントを参照)。他の人がそれを追いかける必要がないように、私は答えを残しています。

更新 変数とそのアドレスに関する混乱を避けるために、Masonのテスト手順を次のように書き直しました。

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;

最初の3つは、メイソンが説明する結果を生成します。最後のwritelnのポインター値を表示するためにwritelnを追加しただけです。TryRTTIStuffの最後の呼び出しは、有効なTypeInfo構造体へのポインターを渡さない場合、呼び出しの1番目と3番目のwritelnで同じ値を取得しないことを示すことです。

TypeInfoで何が起こっているかについてはまだ手がかりがありません。新しいD2010RTTIの作成者であるBarryKellyに尋ねる必要があるかもしれません。したがって、古いものについても多くのことを知っている必要があります...

于 2010-08-09T19:43:55.377 に答える
0

たまたま連続したメモリにあるリンクリスト:)

于 2010-08-09T19:04:08.203 に答える