开发者

Delphi: non-deterministic access violation using RTTI to set object properties from TMemo.Text

I'm building a really crude GUI to model mapper which basically traverses all TEdit and TMemo fields on a form, extracts the text and set this text in a data model object. (The solution hinges on am admittedly brittle "convention over configuration" approach, matching only the properties in the data model that has identical name with a field in the form.)

Disclaimer: sorry about the bloated code example. Here goes:

The form.

{ Standard interface section above this line }
type
  TfrmMain = class(TForm)
    StringField1: TEdit;
    StringField2: TMemo;
    { Other fields and procedures dropped for brevity }
  private
    procedure FillGUIFields();
  end;

The data model.

TDataModel = class(TObject)
private
  FStringField1: string;
  FStringField2: string;
  { Getters and setters dropped for brevity }
public
  property StringField1: string read GetFStringField1 write SetFStringField1;
  property StringField2: string read GetFStringField2 write SetFStringField2;
end;

The implementation.

const
  MAX_RUNS = 100;

procedure GUIToData(var AObject: TObject; const Form: TForm);
var
  c:          TRTTIContext;
  t:          TRTTIType;
  prop:       TRTTIProperty;
  Component:  TComponent;
  Text:       s开发者_运维技巧tring;
  i:          integer;
begin
  c := TRTTIContext.Create();
  t := c.GetType(AObject.ClassType);
  for prop in t.GetProperties do begin
    Component := Form.FindComponent(prop.Name); // Naive "conv. over conf." matching
    if (Component <> nil) then begin
      if (Component is TEdit) then prop.SetValue(AObject, TValue.FromVariant(TEdit    (Component).Text));
      if (Component is TMemo) then prop.SetValue(AObject, TValue.FromVariant(TMemo(Component).Text));
    end;
  end;
  c.Free();
end;

procedure TfrmMain.btnFetchToModelClick(Sender: TObject);
var
  Data:               TDataModel;
  i:                  integer;
  NumberOfExceptions: integer;
begin
  NumberOfExceptions := 0;
  for i := 0 to MAX_RUNS - 1 do begin
    try
      FillGUIFields();
      Data := TDataModel.Create();
      GUIToData(TObject(Data), self);
      Data.Free();
    except on E: EAccessViolation do
      begin
        Inc(NumberOfExceptions);
      end;
    end;
  end;
  MessageDlg('Number of runs: ' + IntToStr(MAX_RUNS) + #13#10 +
             'Number of exceptions: ' + IntToStr(NumberOfExceptions), mtInformation, [mbOk], 0);
end;

function TDataModel.GetFStringField1: string;
begin
  Result := FStringField1;
end;

procedure TDataModel.SetFStringField1(Value: string);
begin
  FStringField1 := Value;
end;

{ Identical getter/setter for StringField2 }

procedure TfrmMain.FillGUIFields;
var
  i:          integer;
  TempBuffer: string;
begin
  TempBuffer := '';
  Randomize();
  for i := 0 to Random(16) - 1 do begin
    if Random(2) = 0 then
      TempBuffer := TempBuffer + Chr(Random(25) + 65)
    else
      TempBuffer := TempBuffer + Chr(Random(25) + 97);
  end;
  StringField1.Text := TempBuffer; // Filling the edit field
  { Identical code for filling the memo field }
end;

end.

When I run this, I get an access violation exception in something like 27 % of the cases. If I only set the property matching the name of the TEdit-field (i.e. StringField1), no exceptions occur. If I access the fields directly (either letting the getter/setter point directly at the fields or using t.GetFields in the GUIToData-procedure), no access violation is thrown.

Are people able to reproduce this? Do anyone know what causes this strange behavior? Thanks!


Ok. So here's what's happened.

Restarting RAD Studio did not solve the problem - i.e. I was still able to reproduce. Then I asked a colleague to compile the project on his computer, no exceptions occurred. Same code and same RAD Studio version (apparently - we where both running D2010 Version 14.0.3513.24210). Then I asked my colleague to run my failing executable on his machine, it behaved exactly as on my computer. (We compared the exes from his and my computer in a hex editor, which revealed pretty glaring discrepancies.)

Then, we compared the Win32 sources bundled with D2010. A lot of discrepancies there as well, in particular in Classes.pas and RTTI.pas.

Something had to be wrong with my D2010 setup. Solution? Running RAD Studio 2010 Update 4 solved my problem (now running D2010 Version 14.0.3593.25826). The cause? I guess I'll never know.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜