Основной механизм соединения с Cache' при построении клиент-серверных систем и традиционных средств разработки класса Delphi, Borland C++ Builder и других - это ActiveX-комплект CacheObject.
При его использовании без VB может быть потеряна функциональность callback, а именно так называемый механизм получения терминального вывода процесса. В VB используется конструкция
При вызове на сервере метода какого-либо класса, который возвращает информацию не в виде возврата функции, а отправляя в текущее устройство вывода, например, $System.OBJ.ExportCDL(), объект CacheFactory перенаправляет вывод в компонент VB, указанный в SetOutput. Поскольку в Delphi и BCB нет бейсикового textbox, возникает проблема.
Ее решение заключается в имитации компонента, который в бейсиковом варианте передается в SetOutput. Для ActiveX компонента это выглядит естественным образом как некий IDispatch - объект. То есть для получения такого терминально-ориентированного вывода нужно «подсунуть» некий объект, реализующий IDispatch интерфейс и который по правилам и соглашениям IDispatch воспроизведет ту функциональность бейсикового textbox, которую использует CacheObject.
Реализация IDispatch на BCB и Delphi не сложна, достаточно следовать документации, описывающей создание automation-объектов. Для получения активации нам нужно реализовать функции IDispatch.
При его использовании без VB может быть потеряна функциональность callback, а именно так называемый механизм получения терминального вывода процесса. В VB используется конструкция
set CacheFactory = CreateObject("CacheObject.Factory") CacheFactory.SetOutput( TextBox)Здесь TextBox - это компонент многострочного вывода.
При вызове на сервере метода какого-либо класса, который возвращает информацию не в виде возврата функции, а отправляя в текущее устройство вывода, например, $System.OBJ.ExportCDL(), объект CacheFactory перенаправляет вывод в компонент VB, указанный в SetOutput. Поскольку в Delphi и BCB нет бейсикового textbox, возникает проблема.
Ее решение заключается в имитации компонента, который в бейсиковом варианте передается в SetOutput. Для ActiveX компонента это выглядит естественным образом как некий IDispatch - объект. То есть для получения такого терминально-ориентированного вывода нужно «подсунуть» некий объект, реализующий IDispatch интерфейс и который по правилам и соглашениям IDispatch воспроизведет ту функциональность бейсикового textbox, которую использует CacheObject.
Реализация IDispatch на BCB и Delphi не сложна, достаточно следовать документации, описывающей создание automation-объектов. Для получения активации нам нужно реализовать функции IDispatch.
TSetOutput = class ( TInterfacedObject, IDispatch) protected function GetTypeInfoCount( out Count: Integer): HResult; stdcall; function GetTypeInfo( Index, LocaleID: Integer; out TypeInfo): HResult; stdcall; function GetIDsOfNames( const IID: TGUID; Names: Pointer; NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall; function Invoke( DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall; end;Как показывает анализ поведения создаваемого объекта этого класса, компонент CacheObject.Factory использует только свойство Text. Поэтому достаточно реализовать поддержку только его: в обработчике GetIDsOfNames сообщить первый свободный dispid
function TSetOutput.GetIDsOfNames( const IID: TGUID; Names: Pointer; NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; type TDispIDsArray = array[0..0] of TDISPID; PDispIDsArray = ^TDispIDsArray; var IDs: PDispIDsArray absolute DispIDs; Name: WideString; begin if NameCount > 1 then Result := DISP_E_UNKNOWNNAME else if NameCount < 1 then Result := E_INVALIDARG else Result := S_OK; if NameCount = 1 then begin Name := PWideChar(Names^); if UpperCase(Name) = 'TEXT' then IDs[0] := 1 else Result := DISP_E_UNKNOWNNAME; end; end;и в обработчике Invoke просто вызвать перенаправление из IDispatch - объекта в обычный дельфийский объект с помощью вызова устанавливаемого обработчикка события.
function TSetOutput.Invoke( DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; var P: TDISPPARAMS absolute Params; V: ^Variant absolute VarResult; S: string; begin if DispID = 1 then begin case Flags of DISPATCH_PROPERTYGET: begin V^ := Variant( ''); end; DISPATCH_PROPERTYPUT: if Assigned( CacheOutput) then begin S := OleVariant( P.rgvarg[ 0]); CacheOutput( S); end; end; Result := S_OK; end else Result := DISP_E_MEMBERNOTFOUND; end;Программный интерфейс компонента TSetOutput выполнен таким образом, чтобы его можно было использовать как есть, просто добавив пару строк кода к своему приложению и написав обработчик события в обычном дельфийском стиле, например если есть форма с кнопкой и одним компонентом многострочного редактора, а на сервере есть класс вида
class August.NewClass3 { super = %RegisteredObject; method Dump { code = [ w 123,! w 456,! q ] } method GetDate { returntype = %String; code = [ q $zdt($h,2) ] } }То для демонстрации получения callback-функциональности можно использовать такой код:
procedure TForm1.Button1Click(Sender: TObject); var CacheFactory: Variant; SetOutput: TSetOutput; Obj: Variant; begin CacheFactory := CreateOleObject('CacheObject.Factory'); CacheFactory.Connect( CacheFactory.ConnectDlg()); SetOutput := TSetOutput.Create( OnCacheOutput); CacheFactory.SetOutput( SetOutput as IDispatch); Obj := CacheFactory.New( 'August.NewClass3'); Memo1.Lines.Add( Obj.GetDate); Obj.Dump; CacheFactory.SetOutput( NULL); SetOutput.Free; end;Где используется обработчик событий составленный по обычным дельфийским соглашениям:
procedure TForm1.OnCacheOutput(CacheString: string); begin Memo1.Text := Memo1.Text + CacheString; end;В приведенной реализации компонент TSetOutput не реализует в действительности методы AddRef и Release, поскольку предназначен для работы скорее в качестве стандартного дельфийского объекта, который и создается и удаляется кодом, контролируемым программистом. Отметим, что класс допускает переназначение обработчика события на другой. Приведенный код проверялся на Cache' 5. По сообщениям других программистов, в Cache' 4 такой подход также работает.
Комментариев нет:
Отправить комментарий