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 传递到此例程中,它将输出相同的地址两次。我在 TypInfo.pas 中浏览了一下,但我没有看到任何提到这个“身份指针”或它的用途的东西。有谁知道为什么会这样?这在至少从 D3 到 D2010 的每个版本的 Delphi 中似乎都是正确的。

4

3 回答 3

12

这很简单:包和动态链接。

BPL 是 DLL。DLL 是通过正在修补的表链接起来的,而不是 EXE 中的所有代码或 DLL 链接到正在修补的 DLL(这将对在多个进程之间共享只读内存造成很大的危害)。为了防止TypeInfo(SomeType)在链接 BPL 时需要对代码中某处的引用或 EXE 或 DLL 的类型信息进行修改,而是通过导入表进行间接修改。

在此程序中,静态链接与针对 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

不完全理解发生了什么,但是当您查看单元中的示例IsPublishedPropTypInfo,您会看到它将实例的 ClassInfo 转换为指向 TypeInfo 结构的指针:

PTypeInfo(Instance.ClassInfo)

当您查看 ClassInfo 方法时,它返回一个简单的指针,其值似乎与 vmt 表相关:

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

vmtTypeInfo值为 -72。在 -76 之前的四个字节是vmtInitTable. vmtTypeInfo 之后是 FieldTable、MethodTable、DynamicTable 等。

vmtInitTable 值用于例如TObject.CleanupInstance_FinalizeRecord作为指向 TypeInfo 结构的指针传递给。

因此,指向 TypeInfo 结构的 TypeInfo 结构之前的四个字节似乎是设计使然,并且是 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;

前三个产生了梅森描述的结果。我只添加了一个额外的 writeln 来显示最后一个 writeln 的指针值。TryRTTIStuff 中的最后一个调用是为了表明,当您不传递指向有效 TypeInfo 结构的指针时,您不会在调用的第一个和第三个 writeln 上获得相同的值。

尚不知道 TypeInfo 发生了什么。也许我们应该问巴里凯利,因为他是新 D2010 RTTI 的作者,所以也应该对旧的有很多了解......

于 2010-08-09T19:43:55.377 回答
0

也许它是一个恰好位于连续内存中的链表:)

于 2010-08-09T19:04:08.203 回答