Сериализация объектов
От: Verder  
Дата: 08.11.05 14:43
Оценка:
Доброго времени суток.

Просмотрел статью http://www.rsdn.ru/?article/delphi/serialization.xml, очень позновательно, но вот, что меня интересует. Каким образом сохраняются и восстанавливаются обработчики, например, Button1Click? Delphi каким-то образом это делает, отображаю в инспекторе назначенный на OnClick Button1Click!

Подскажите кто знает!
Re: Сериализация объектов
От: Danchik Украина  
Дата: 08.11.05 16:36
Оценка:
Здравствуйте, Verder, Вы писали:

V>Доброго времени суток.


V>Просмотрел статью http://www.rsdn.ru/?article/delphi/serialization.xml, очень позновательно, но вот, что меня интересует. Каким образом сохраняются и восстанавливаются обработчики, например, Button1Click? Delphi каким-то образом это делает, отображаю в инспекторе назначенный на OnClick Button1Click!


V>Подскажите кто знает!


Кажется так получится, но я до конца не уверен...
procedure TForm1.OnFindMethod(Reader: TReader; const MethodName: string;
  var Address: Pointer; var Error: Boolean);
begin
  Address := Self.MethodAddress (MethodName);
  Error := False;
end;

// пример чтения из потока

  aReader := TReader.Create(aStream {поток с данныти}, 4096);
  try
  //  aReader.OnError := OnReaderError; возможность перехватывать ошибки чтения
    aReader.OnFindMethod := OnFindMethod; // возможность подсунуть свое событие
    aComponent := aReader.ReadRootComponent (nil); {создаем обьект из потока}
  finally
    aReader.Free;
  end;

Только не забывай MethodAddress работает только с published методами.
Re[2]: Сериализация объектов
От: Verder  
Дата: 08.11.05 19:40
Оценка:
D>Кажется так получится, но я до конца не уверен...
D>
D>procedure TForm1.OnFindMethod(Reader: TReader; const MethodName: string;
D>  var Address: Pointer; var Error: Boolean);
D>begin
D>  Address := Self.MethodAddress (MethodName);
D>  Error := False;
D>end;

D>// пример чтения из потока

D>  aReader := TReader.Create(aStream {поток с данныти}, 4096);
D>  try
D>  //  aReader.OnError := OnReaderError; возможность перехватывать ошибки чтения
D>    aReader.OnFindMethod := OnFindMethod; // возможность подсунуть свое событие
D>    aComponent := aReader.ReadRootComponent (nil); {создаем обьект из потока}
D>  finally
D>    aReader.Free;
D>  end;
D>

D>Только не забывай MethodAddress работает только с published методами.

Я знаю, что так можно, но в этом случае функция/метод
Address := Self.MethodAddress (MethodName);
определена в классе, который читает форму. Меня интересует каким образом Delphi адресует обработчики, отображая их в инспекторе объектов.
Re[3]: Сериализация объектов
От: icWasya  
Дата: 09.11.05 09:23
Оценка:
Здравствуйте, Verder, Вы писали:

> Меня интересует каким образом Delphi адресует обработчики, отображая их в инспекторе объектов.

А тут скорее всего анализируется исходный код юнита
Re[3]: Сериализация объектов
От: Danchik Украина  
Дата: 09.11.05 12:51
Оценка:
Здравствуйте, Verder, Вы писали:

[Skip]

V>Я знаю, что так можно, но в этом случае функция/метод

V>
V>Address := Self.MethodAddress (MethodName);
V>
определена в классе, который читает форму. Меня интересует каким образом Delphi адресует обработчики, отображая их в инспекторе объектов.


Для отображения в инспекторе Delphi использует внутренний парсер. Тоесть постоянно парсает сурцы чтобы определить доступные published методы. В DFM сохраняеются именя событий, которые вотстанавливает используя MethodAddress.

Автоматический подъем формы происхорит потому, что свойство TReader.Root (именно у него требуется MethodAddress) проинициализировано в первую поднятую компоненту — форму. А так как события именно там и преопределены, то все подымается без проблем.

В методе TReader.FindMethod все и видно:
function TReader.FindMethod(Root: TComponent;
  const MethodName: string): Pointer;
var
  Error: Boolean;
begin
  Result := Root.MethodAddress(MethodName);
  Error := Result = nil;
  if Assigned(FOnFindMethod) then FOnFindMethod(Self, MethodName, Result, Error);
  if Error then PropValueError;
end;


Для того чтобы определить какие параметры необходимы для событий используется RTTI. В RTTI сохранена полная строка параметров.
Вот код из закромов родины:
function GetEventPrototype(aObject : TObject; const aEventName : string): string;
var
  pInfo: PPropInfo;
  pData: PTypeData;
  ParamFlags: TParamFlags;
  pName: PShortString;
  pScan: PByte;
  S: string;
  i: Integer;
begin
  pInfo := GetPropInfo(aObject.ClassInfo, aEventName);
  if Assigned(pInfo) then begin
    if pInfo^.PropType^.Kind = tkMethod then begin
      pData := GetTypeData(pInfo^.PropType^);
      if Assigned(pData) then begin
        case pData^.MethodKind of
          mkProcedure: S := 'procedure ';
          mkFunction: S  := 'function ';
          else
            S := GetEnumName(Typeinfo(TMethodKind), Ord(pData^.MethodKind));
            S := S + ', unexpected for a event handler.';
            raise Exception.Create(S);
        end;

        pScan := @pData^.ParamList;
        if pData^.ParamCount > 0 then begin
          S := S + '(';
          for i := 1 to pData^.ParamCount do begin
            ParamFlags := TParamFlags(pScan^);
            Inc(pScan, Sizeof(TParamFlags));
            if pfVar In ParamFlags then
              S := S + 'var '
            else if pfConst In ParamFlags then
              S := S + 'const ';
            pName := pShortString(pScan);
            S := S + pName^ + ': ';
            Inc(pScan, Length(pName^) + 1);
            if pfArray In ParamFlags then
              S := S + 'array of ';
            pName := pShortString(pScan);
            S := S + pName^;
            Inc(pScan, Length(pName^) + 1);
            if i <> pData^.ParamCount then
              S := S + '; ';
          end;
          S := S + ')';
        end;

        if pData^.MethodKind = mkFunction then begin
          pName := pShortString(pScan);
          S     := S + ': ' + pName^;
        end;

        //        S:= S + ' of Object;';
        Result := S;
      end;
    end else
      raise Exception.CreateFmt('Property %s.%s is not an event.', [
        aObject.ClassName, aEventName]);
  end else
    raise Exception.CreateFmt('Class %s had no property %s.', [aObject.ClassName, aEventName]);
end;
Re: Сериализация объектов
От: Verder  
Дата: 09.11.05 14:22
Оценка:
Что у меня получилось. Есть главная формочка, она умеет создавать форму, DFM-ка которой лежит в ресурсах DLL-ки:

Главная формочка
procedure TmForm.Button4Click(Sender: TObject);
var
   ResForm: TsForm;
   rs: TResourceStream;
   ms: TMemoryStream;
   rd: TReader;
   tm: TMethod;
begin
   rs := TResourceStream.Create( h, ResName, RT_RCDATA );
   try
      ms := TMemoryStream.Create;
      try
         ObjectTextToBinary( rs, ms );
         ms.Seek( 0, soFromBeginning );
         ResForm := TsForm.CreateNew( Self );
         try
            rd := TReader.Create( ms, 4096 );
            try
               RegisterClass( TsForm );
               tm.Code := @SubForm.FindMethod;
               tm.Data := ResForm;
               rd.OnFindMethod := TFindMethodEvent( tm );
               ResForm := TsForm( rd.ReadRootComponent( ResForm ) );
               ResForm.ShowModal;
            finally
               rd.Free;
            end;
         finally
            ResForm.Free;
         end;
      finally
         ms.Free;
      end;
   finally
      rs.Free;
   end;
end;
Модуль с утилитами:
procedure FindMethod(Obj: TObject; Reader: TReader; const MethodName: string;
  var Address: Pointer; var Error: Boolean);
begin
   Address := Obj.MethodAddress( MethodName );
   Error := false;
end;
Получаю формочку из DLL-ки с работающим обработчиком на кнопке.
Недостаток данного подхода следующий. Если разместить в DLL-ке как форму предка, так и пронаследованного от него потомка и попытаться получить пронаследованную форму, то форма будет пустой, без компонентов от предка.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.