function Invoke
После получения идентификатора запрошенного метода OLE вызывает функцию Invoke, передавая в неё:
DispID
Идентификатор вызываемого метода или свойства, полученный от GetIdsOfNames
LocaleId
Региональный контекст (тот же, что и в GetIdsOfNames)
Flags
Битовая маска, состоящая из следующих флагов
Значение Комментарий
DISPATCH_METHOD Вызывается метод. Если у объекта есть свойство с таким же именем, то будет установлен также флаг DISPATCH_PROPERTYGET
DISPATCH_PROPERTYGET Запрашивается значение свойства
DISPATCH_PROPERTYPUT Устанавливается значение свойства
DISPATCH_PROPERTYPUTREF Параметр передается по ссылке. Если флаг не установлен – по значению
Params
Структура DISPPARAMS, содержащая массив параметров, массив идентификаторов для именованных параметров, и количества элементов в этих массивах. Параметры передаются в порядке, обратном их порядку следования в функции, как это принято в Visual Basic
VarResult
Адрес переменной типа OleVariant, в которую должен быть помещен результат вызова метода или значение свойства или NIL, если возвращаемое значение не требуется.
ExcepInfo
Адрес структуры EXCEPTINFO, которую метод должен заполнить информацией об ошибке, если она возникнет.
ArgErr
Адрес массива, в который должны быть помещены индексы неверных параметров, в случае, если такая ситуация будет обнаружена.
При вызове Invoke не осуществляется никаких проверок, поэтому при его самостоятельной реализации необходимо соблюдать аккуратность при работе с переданными адресами массивов и переменных.
Как видно из описания IDispatch – имеется возможность самостоятельно реализовать этот интерфейс, динамически преобразуя обращения к объекту автоматизации в обращения к соответствующим свойствам классов Delphi.
Информация RTTI Delphi
Delphi имеет свой внутренний протокол, позволяющий осуществлять обращение к опубликованным (объявленным в секции published) свойствам и методам класса. Для этого служат функции модуля TypInfo.pas. Ключевой является функция
function GetPropInfo(TypeInfo: PTypeInfo;
const PropName: String): PPropInfo;
которая позволяет по имени свойства получить адрес структуры PPropInfo, содержащей информацию о свойстве. В дальнейшем можно получить значение этого свойства при помощи функций GetXXXProp или установить его функциями SetXXXProp. При этом будут корректно вызваны функции получения или установки свойства. Таким образом, у нас есть возможность по имени свойства определить его наличие и установить или получить его значение. Такая возможность позволяет нам создать реализацию IDispatch, динамически транслирующую обращения к свойствам зарегистрированного в TScriptControl объекта автоматизации в обращения к свойствам связанного с ним экземпляра класса VCL
Сводим воедино
Итак, как рассмотрено выше – RTTI Delphi предоставляет достаточную функциональность для того, чтобы обеспечить трансляцию вызовов OLE-Automation в обращения к свойствам компонентов VCL. Для этого необходимо:
1. | В методе GetIdsOfNames проверить существование свойства, при помощи функции GetPropInfo и, если такое свойство найдено – вернуть какой-нибудь числовой идентификатор. В роли такого идентификатора удобно использовать результат, возвращаемый функцией GetPropInfo. |
2. | В методе Invoke – установить или получить значение свойства, используя функции GetXXXProp или SetXXXProp. |
Для трансляции вызовов OLE в VCL создадим класс TVCLProxy
Code: |
type // Этот интерфейс понадобится для получения ссылки на // класс VCL из методов, в которые передается его // интерфейс IDispatch IQueryPersistent = interface ['{26F5B6E1-9DA5-11D3-BCAD-00902759A497}'] function GetPersistent: TPersistent; end;
TVCLProxy = class(TInterfacedObject, IDispatch, IQueryPersistent) private FOwner: TPersistent; FScriptControl: TVCLScriptControl; procedure DoCreateControl(AName, AClassName: WideString; WithEvents: Boolean); function SetVCLProperty(PropInfo: PPropInfo; Argument: TVariantArg): HRESULT; function GetVCLProperty(PropInfo: PPropInfo; dps: TDispParams; PDispIds: PDispIdList; var Value: OleVariant): HRESULT; { IDispatch } 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; { IQueryPersistent } function GetPersistent: TPersistent; protected function DoInvoke (DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var dps : TDispParams; pDispIds : PDispIdList; VarResult, ExcepInfo, ArgErr: Pointer): HResult; virtual; public constructor Create(AOwner: TPersistent; ScriptControl: TVCLScriptControl); destructor Destroy; override; end; |
Экземпляр этого класса создается при регистрации объекта в TScriptControl и уничтожается автоматически, когда необходимость в нем исчезает.
Поле FOwner хранит ссылку на экземпляр класса VCL, интерфейс к которому предоставляет этот объект. TVCLScriptControl – это наследник TScriptControl.
Главным его отличием является наличие списка зарегистрированных экземпляров TVCLProxy и обработчиков событий, позволяющих компонентам VCL вызывать методы скрипта.
Здесь рассмотрены лишь ключевые моменты реализации, полный код, вместе с примером использования, приведен на компакт диске.
Просьба писать ваши замечания, наблюдения и все остальное,
что поможет улучшить предоставляемую информацию на этом сайте.
ВСЕ КОММЕНТАРИИ МОДЕРИРУЮТСЯ ВРУЧНУЮ, ТАК ЧТО СПАМИТЬ БЕСПОЛЕЗНО!