Delphi: недетерминированное нарушение доступа с использованием RTTI для установки свойств объекта из TMemo.Text - PullRequest
1 голос
/ 06 октября 2011

Я создаю очень грубый графический интерфейс для картографа модели, который в основном пересекает все поля TEdit и TMemo в форме, извлекает текст и устанавливает этот текст в объект модели данных.(Решение основано на предположительно хрупком подходе «соглашение о конфигурации», в котором сопоставляются только свойства в модели данных, имя которой совпадает с полем в форме.)

Отказ от ответственности: извините за пример раздутого кода.Здесь идет:

Форма.

{ 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;

Модель данных.

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;

Реализация.

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.

Когда я запускаю этуЯ получаю исключение нарушения прав доступа в 27% случаев.Если я только устанавливаю свойство, соответствующее имени поля TEdit (т. Е. StringField1), исключений не возникает.Если я обращаюсь к полям напрямую (либо позволяя получателю / установщику указывать прямо на поля, либо используя t.GetFields в GUIToData-процедуре), нарушение доступа не выдается.

Могут ли люди это воспроизвести?Кто-нибудь знает, что вызывает это странное поведение?Спасибо!

1 Ответ

1 голос
/ 07 октября 2011

Хорошо. Итак, вот что случилось.

Перезапуск RAD Studio не решил проблему - то есть я все еще смог воспроизвести. Затем я попросил коллегу скомпилировать проект на своем компьютере, исключений не произошло. Тот же код и та же версия RAD Studio (по-видимому - мы оба работали под D2010 версии 14.0.3513.24210). Затем я попросил моего коллегу запустить мой сбойный исполняемый файл на его компьютере, он вел себя точно так же, как на моем компьютере. (Мы сравнили exes с его и моего компьютера в шестнадцатеричном редакторе, который выявил довольно явные расхождения.)

Затем мы сравнили источники Win32 в комплекте с D2010. Там также много расхождений, в частности в Classes.pas и RTTI.pas.

Что-то должно быть не так с моей настройкой D2010. Решение? Запуск RAD Studio 2010 Update 4 решил мою проблему (сейчас работает D2010 версии 14.0.3593.25826). Причина? Я думаю, я никогда не узнаю.

...