Delphi 什么是「;“标识指针”;在一个TTypeInfo之前,你在那里做什么?

Delphi 什么是「;“标识指针”;在一个TTypeInfo之前,你在那里做什么?,delphi,pointers,rtti,internals,Delphi,Pointers,Rtti,Internals,如果您深入研究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); d

如果您深入研究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中都是如此。

可能是一个恰好在连续内存中的链表:)

不完全理解正在发生的事情,但是当您查看
TypInfo
单元中的
IsPublishedProp
时,您将看到它将实例的ClassInfo强制转换为指向TypeInfo结构的指针:

PTypeInfo(Instance.ClassInfo)
查看ClassInfo方法时,它返回一个简单指针,其值似乎与vmt表相关:

  Result := PPointer(Integer(Self) + vmtTypeInfo)^;
vmtTypeInfo
的值为-72。在-76之前的四个字节是
vmtinitable
。vmtTypeInfo后面是FieldTable、MethodTable、DynamicTable等

VMTinitable值在例如
ToObject.CleanupInstance
中使用,并作为指向TypeInfo结构的指针传递给
\u FinalizerRecord

因此,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的指针值。tryrttitStuff中的最后一个调用是显示当您没有传入指向有效TypeInfo结构的指针时,您不会在调用的第一个和第三个writeln上获得相同的值


关于TypeInfo到底发生了什么还没有线索。也许我们应该问问Barry Kelly,因为他是新D2010 RTTI的作者,所以应该对旧版本也了解很多…

很简单:包和动态链接

BPL是DLL。DLL通过被修补的表进行链接,而不是EXE或DLL中的所有代码针对被修补的DLL进行链接(这将对多个进程之间共享只读内存造成极大危害)。为了防止在与BPL链接时修改代码中某处的
TypeInfo(SomeType)
引用或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
TObject
x  004051F0
x^ 40001DA4
但是当使用带有
dcc32-LUrtl test.pas的RTL包编译时,它会输出:

TObject
x  00401B64
x^ 00401B68
TObject
x  004051F0
x^ 40001DA4

希望这能澄清问题。

不,因为在TTypeInfo/TTypeData结构中没有类似于“下一个指针”的东西。不过这个想法很有趣。我假设上面的代码中缺少一个
var
。@Andreas:Where?我没有看到任何丢失的VAR…抱歉,但你看错了东西。TypeInfo与VMT无关,因为它也可以用于非对象类型。VMT包含指向类的TTypeInfo记录的指针,但这不是我要问的。@Mason:你可能完全正确,但我认为“老式RTTI”只支持类和枚举的TypeInfo?只有在枚举声明中未指定特定值时,才使用后者。我想其他类型的RTTI直到2010年才出现(我使用的是D2009)。类型信息必须适用于DFM中可以使用的任何基本类型,因为这就是系统发明的目的:表单序列化和反序列化。我原以为TTypeTypes“只是”类属性的描述符,但基本类型确实有自己的TypeInfo结构(我一边玩代码一边玩)。可惜TypeInfo方法是编译器的魔法,不是吗…是的,Barry经常出现在这里回答问题。我有点认为他或艾伦·鲍尔可能是唯一能对这个问题给出有效答案的人(我只是想表达一个非常类似的想法。谢谢你的洞察力。所以指针必须在某个地方,链接器才能完成它的工作,而且它恰好放在它指向的数据之前,当数据在同一个模块中时,按照惯例?好的,这是有意义的。谢谢。@Mason所有的typeinfo修正-来自一个模块的指针typeinfo到另一个的blob-是PPTypeInfo类型,而不是PTypeInfo类型,用于处理动态链接情况。在静态链接的情况下,需要有一个中间指针才能使约定生效,并且typeinfo本身的一部分与任何类型一样有意义。也就是说,它不用于链接器;它之所以存在是因为约定,而这种约定之所以存在,是因为动态链接,它是以最大化页面共享潜力的方式进行的。啊,这就是PPTypeInfo的作用。谢谢您的解释。