1

I have a very complex nested record type. I need to monitor changes in values of it's fields after a method call. I used RTTI to do that (originally copied from here):

procedure CompareFields(const Rec1, Rec2: Pointer; const RectTypeInfo: TRttiType;
  const BaseName: string; const BaseOffset: Integer; const Lines: TStrings);
var
  R1Field, R2Field: Pointer;
  MyField: TRttiField;
  State: string;
  Path: string;
begin
  if RectTypeInfo.TypeKind = tkRecord then
  begin
    for MyField in RectTypeInfo.GetFields do
    begin
      R1Field := Pointer(Integer(Rec1) + BaseOffset + MyField.Offset);
      R2Field := Pointer(Integer(Rec2) + BaseOffset + MyField.Offset);
      if CompareMem(R1Field, R2Field, MyField.FieldType.TypeSize) then
        State := '=='
       else
         State :=  '<>';
      Path := BaseName + '.' + MyField.Name;

      Lines.Add(Format('%2s | %-20s | +%2d | %10s[%2d] | %22s | %22s', [State, Path,
        BaseOffset + MyField.Offset,
        MyField.FieldType.ToString,
        MyField.FieldType.TypeSize,
        MyField.GetValue(R1Field).ToString,
        MyField.GetValue(R2Field).ToString]));

      if MyField.FieldType.TypeKind = tkRecord then
        CompareFields(Rec1, Rec2, MyField.FieldType, Path, BaseOffset + MyField.Offset, Lines);
    end;
  end;
end;

The CompareMem part works fine and clearly indicates the fields that have been changed. But the problem is about reporting values of tow record. MyField.GetValue(R1Field).ToString some times return a random value and that's because MyField.GetValue(R1Field) fill the result with random values. This is a sample that demonstrates the problem:

type
  TMyRec1 = record
    F11: Integer;
    F12: Boolean;
    F13: Double;
  end;

  TMyRec2 = record
    F21, F22: TMyRec1;
    F23: Integer;
    F24: TMyRec1;
  end;

procedure TForm1.Button1Click(Sender: TObject);
var
  MyContext: TRttiContext;
  Rec1, Rec2: TMyRec2;
begin
  FillChar(Rec1, SizeOf(Rec1), #0);
  FillChar(Rec2, SizeOf(Rec1), #0);

  CompareFields(@Rec1, @Rec2, MyContext.GetType(TypeInfo(TMyRec2)), 'TMyRec2', 0,
    Memo1.Lines);

end;

and the result is:

== | TMyRec2.F21          | + 0 |    TMyRec1[16] |               (record) |               (record)
== | TMyRec2.F21.F11      | + 0 |    Integer[ 4] |                      0 |                      0
== | TMyRec2.F21.F12      | + 4 |    Boolean[ 1] |                  False |                  False
== | TMyRec2.F21.F13      | + 8 |     Double[ 8] |                      0 |                      0
== | TMyRec2.F22          | +16 |    TMyRec1[16] |               (record) |               (record)
== | TMyRec2.F22.F11      | +16 |    Integer[ 4] |                      0 |                      0
== | TMyRec2.F22.F12      | +20 |    Boolean[ 1] |                  False |                  False
== | TMyRec2.F22.F13      | +24 |     Double[ 8] |                      0 |                      0
== | TMyRec2.F23          | +32 |    Integer[ 4] |               21013568 |                      0 <<--
== | TMyRec2.F24          | +40 |    TMyRec1[16] |               (record) |               (record)
== | TMyRec2.F24.F11      | +40 |    Integer[ 4] |                      0 |                      0
== | TMyRec2.F24.F12      | +44 |    Boolean[ 1] |                  False |                  False
== | TMyRec2.F24.F13      | +48 |     Double[ 8] |  9.92338439963001E-303 |                      0 <<--

As you can see TMyRec2.F23 and TMyRec2.F24.F13 seem to have values other than '0' even after calling FillChar!

Community
  • 1
  • 1
saastn
  • 5,717
  • 8
  • 47
  • 78
  • 1
    Please show a [mcve], a console app with Writeln output, and then we can run the code with minimal effort. – David Heffernan Feb 08 '16 at 01:49
  • 1
    Which version of Delphi are you using? The code shown works fine for me as-is in XE2, `TMyRec2.F23` and `TMyRec2.F24.F13` are both 0 as expected. – Remy Lebeau Feb 08 '16 at 02:51

0 Answers0