代码之家  ›  专栏  ›  技术社区  ›  Mason Wheeler

TTypeInfo前面的“标识指针”是用来做什么的?

  •  8
  • Mason Wheeler  · 技术社区  · 14 年前

    如果您在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。

    3 回复  |  直到 14 年前
        1
  •  12
  •   Barry Kelly    14 年前

    它非常简单:包和动态链接。

    BPL是DLL。DLL是通过被修补的表链接起来的,而不是EXE或DLL中的所有代码与被修补的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
    

    希望这能解决问题。

        2
  •  1
  •   Marjan Venema    14 年前

    不完全了解发生了什么,但当你看一个例子 IsPublishedProp TypInfo 单元,您将看到它将实例的ClassInfo转换为指向TypeInfo结构的指针:

    PTypeInfo(Instance.ClassInfo)
    

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

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

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

    TObject.CleanupInstance 传给 _FinalizeRecord 作为指向TypeInfo结构的指针。

    因此,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;
    

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

    还不知道TypeInfo是怎么回事。也许我们应该问巴里凯利,因为他是新的D2010 RTTI的作者,所以应该知道很多关于旧的以及。。。

        3
  •  0
  •   koo5    14 年前

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