Delphi XE2 RTTI сломан?

недавно я мигрировал из D2010 в DXE2 и обнаружил ошибку showstopper (или функцию?) в XE2 и XE3 (проверено в my friend XE3), связанных с генерацией RTTI для полей TBytes внутри классов.

Я обнаружил, что информация RTTI для переменной TBytes внутри класса никогда не генерируется.

следующий код хорошо работает в D2010, но показывает сообщение "Ошибка" в XE2/XE3

кто-нибудь знает? Это полностью сломает все наши программные данные реализация сериализации

чтобы проверить код, добавьте Rtti в декларацию uses

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;

сообщение об ошибке будет отображаться при проверке Field2, который является TBytes, потому что тип поля всегда равен нулю!!!

кто-нибудь имеет представление о том, что изменилось в RTTI с D2010 do XE2? Может быть, потому, что тип TBytes был изменен с массива байтов на общий массив?

2 ответов


Это известная проблема, которая была исправлена в XE3. к сожалению, обновление-единственный способ исправить это; исправления ошибок обычно не переносятся обратно.

EDIT: или нет. По-видимому, это на самом деле не исправлено, так как это все еще происходит в XE3. Сообщить об этом как о новом случае и упомянуть 103729, вероятно, было бы лучшим вариантом действий.


вы можете исправить эту ошибку (на самом деле это не та же ошибка, что и упомянутый Мейсон).

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