Reputation: 6138
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: string;
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!
Upvotes: 1
Views: 895
Reputation: 6138
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.
Upvotes: 1