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.
精彩评论