Доброго времени суток.
Просмотрел статью
http://www.rsdn.ru/?article/delphi/serialization.xml, очень позновательно, но вот, что меня интересует. Каким образом сохраняются и восстанавливаются обработчики, например, Button1Click? Delphi каким-то образом это делает, отображаю в инспекторе назначенный на OnClick Button1Click!
Подскажите кто знает!
Здравствуйте, 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 методами.
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 адресует обработчики, отображая их в инспекторе объектов.
Здравствуйте, 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;
Что у меня получилось. Есть главная формочка, она умеет создавать форму, 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-ке как форму предка, так и пронаследованного от него потомка и попытаться получить пронаследованную форму, то форма будет пустой, без компонентов от предка.