Delphi XE2的RTTI有问题吗?

12

我最近从D2010迁移到DXE2,并发现了一个严重的 bug (或者特性?) 在XE2和XE3(我朋友的XE3中测试)与类内TBytes字段的 RTTI 生成相关。

我发现类内 TBytes 变量的 RTTI 信息从未被生成。

以下代码在 D2010 中工作良好,但在 XE2/XE3 中显示 "Error" 消息。

有人有任何线索吗?这将完全破坏我们所有的软件数据序列化实现。

为测试该代码,请在 uses 声明中添加 Rtti 单元。

type

  TMyClass = class
  public
    Field1: Integer;
    Field2: TBytes;
  end;


procedure TForm2.Button1Click(Sender: TObject);
var
  i: Integer;
  Data: TMyClass;
  Rtti: TRttiContext;
  RttiClassType: TRttiInstanceType;
begin

  Data := TMyClass.Create;
  try

    // Get the context
    Rtti := TRttiContext.Create;
    try

      // Get the type for the class
      RttiClassType := TRttiInstanceType(Rtti.GetType(Data.ClassInfo));

      // Check the fields
      for i := 0 to High(RttiClassType.GetFields) do
      begin

        // Check the field type
        if not Assigned(RttiClassType.GetFields[i].FieldType) then
          ShowMessage('Error');

      end;

    finally
      Rtti.Free;
    end;

  finally
    Data.Free;
  end;

end;

由于FieldType始终为空,所以在检查TBytes类型的Field2时会显示错误信息!!!

有人知道D2010到XE2中RTTI发生了什么变化吗?也许是因为TBytes类型从字节数组更改为通用数组了吗?


请告诉我们您在哪个QC报告编号下提交的。 - Jeroen Wiert Pluimers
1
当然,我刚刚打开了#109190,但显然它是#97748的重复报告。希望他们能解决这个问题! - Eric
2个回答

15
您可以修复此错误(实际上这并不是Mason提到的那个错误)。
type
  FixTypeInfoAttribute = class(TCustomAttribute)
  public
    FTypeInfo: PPTypeInfo;
    constructor Create(TypeInfo: PTypeInfo);
  end;

procedure FixFieldType(TypeInfo: PTypeInfo);
var
  ctx: TRttiContext;
  t: TRttiType;
  f: TRttiField;
  a: TCustomAttribute;
  n: Cardinal;
begin
  t := ctx.GetType(TypeInfo);
  for f in t.GetFields do
  begin
    for a in f.GetAttributes do
    begin
      if (a is FixTypeInfoAttribute) and f.ClassNameIs('TRttiInstanceFieldEx') then
      begin
        WriteProcessMemory(GetCurrentProcess, @PFieldExEntry(f.Handle).TypeRef,
          @FixTypeInfoAttribute(a).FTypeInfo, SizeOf(Pointer), n);
      end;
    end;
  end;
end;

constructor FixTypeInfoAttribute.Create(TypeInfo: PTypeInfo);
begin
  FTypeInfo := PPTypeInfo(PByte(TypeInfo) - SizeOf(Pointer));
end;

然后您将属性添加到类定义中:
type
  TMyClass = class
  private
    Field1: Integer;
    [FixTypeInfo(TypeInfo(TBytes))]
    Field2: TBytes;
  end;

确保调用FixFieldType例程:

initialization
  FixFieldType(TypeInfo(TMyClass));

在 XE 上进行了测试


你忽略了FixTypeInfoAttribute.Create。而且FixTypeInfoAttributeFixTypeInfo之间存在名称不匹配。但是对于一个好的解决方法,点个赞。 - David Heffernan
2
@David:已添加构造函数的实现。而且,由于属性上可以省略Attribute部分,所以没有名称不匹配的问题。 - Stefan Glienke
通常情况下,使用WriteProcessMemory(GetCurrentProcess, ...)是不必要的,你可以使用Move()或等效函数代替,例如:Move(FixTypeInfoAttribute(a).FTypeInfo, PFieldExEntry(f.Handle).TypeRef, SizeOf(Pointer)); 或者,只需使用简单的赋值语句即可,例如:PFieldExEntry(f.Handle).TypeRef := FixTypeInfoAttribute(a).FTypeInfo; - Remy Lebeau

9

这是一个已在XE3中修复的已知问题(链接)。不幸的是,升级是获得修复的唯一途径;错误修复通常不会被移植回来。

编辑:或者也可能没有。显然,在XE3中仍然存在此问题。报告为新案例并提及103729可能是最好的做法。


1
这里之前有一个帖子,有人告诉你要添加{$RTTI FIELDS([])}。 - Eric
2
而且它确实有效... 问题在于我使用{$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([vcPublic])},以便不为私有类字段生成RTTI... 通过使用vcPublic,它不会为公共TBytes字段生成RTTI... - Eric
2
@Eric:那个“解决方案”是通过根本不为字段生成任何RTTI来实现的。 不为TBytes生成类型是不同的问题; 那是一个编译器级别的问题。 如果在XE3中没有修复,请尝试将此作为新问题发布,并使用您的代码作为示例,同时提及其他QC案例。 - Mason Wheeler
2
通过将TBytes替换为TArray<Byte>,它将生成RTTI。 - Eric
2
TBytes 应该被删除。TArray<Byte> 是声明此类型的规范方式。 - David Heffernan
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接