Этот пост является продолжением моего предыдущего поста, который говорил про RTTI интерфейсов в Delphi и был вдохновлён серией постов Hallvard-а по RTTI. Этот пост рассказывает о некоторых продвинутых возможностях RTTI для классов, описания которых я больше нигде не видел.
$METHODINFO
Когда я игрался с WebSnap в Delphi, пытаясь расширить некоторые из его объектов, доступных для скриптинга, я наткнулся на директиву компилятораMETHODINFO
.Online-справка говорит нам:
ДирективаМой предыдущий пост показал, что это не совсем верно: детализированная RTTI доступна для любого интерфейса с$METHODINFO
эффективна только если была включена генерация RTTI информации директивой{$TYPEINFO ON}
. В режиме{$TYPEINFO ON}
директива$METHODINFO
контролирует генерацию более подробного описания RTTI для методов в интерфейсе. Хотя директива{$TYPEINFO ON}
приведёт к генерации какой-то RTTI информации для published методов, но уровень этой информации ограничен. Включение директивы$METHODINFO
генерирует намного более детализированную (и объёмную) RTTI информацию для методов, которая описывает, как нужно передавать методу его параметры - в стеке или в регистрах. Необходимость использовать переключатель компилятора$METHODINFO
в вашем коде возникает очень редко, если вообще возникает. Подробная информация о методах занимает много места, поэтому включение этого режима не рекомендуется для обычного использования.
$TYPEINFO
или $M
. Кажется, директива $METHODINFO
влияет только на классы, в частности, детализированная информация будет генерироваться не только для published методов, но также для секции public.Произведя поиск этой директивы в файлах исходных кодов Delphi, я нашёл только одно упоминание - в файле
WebSnapObjs.pas
:
{$METHODINFO ON} TScriptableObject = class(TObjectDispatch) private FLookupList: TStringList; FLookupValues: TInterfaceList; protected FPreferChild: Boolean; function DispatchOfName(const AName: string): IDispatch; virtual; function FindObject(const AName: string): TObject; virtual; public constructor Create; destructor Destroy; override; class function DispatchOfObject(const AObject: TObject): IDispatch; function GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount: Integer; LocaleID: Integer; DispIDs: Pointer): HRESULT; override; function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult: Pointer; ExcepInfo: Pointer; ArgErr: Pointer): HRESULT; override; end; {$METHODINFO OFF}
WebSnap
WebSnap - это бедный родственник в мире веб-фреймворков для Delphi. Он никогда не имел большой поддержки, и, кажется, теперь заслоняется ASP.NET и IntraWeb. Мне лично он очень нравится, хотя я создаю мои собственные шаблоны в VBScript или JavaScript, а не использую какие-то веб-страницы из design-time.Под капотом WebSnap использует движок ActiveScripting, предоставляемый Windows. ActiveScripting - это сервер выполнения скриптов, который может выполнять скрипты на любом языке, поддержка которого устанавливается в сервер с помощью COM плагина. В штатной поставке Windows имеется поддержка VBScript и JScript (который, на самом деле, JavaScript). Другие примеры поддержки языков в ActiveScripting включают в себя Python, Perl и PHP (прим.пер.: существуют, но менее распространены реализации движков на языках Basic, Haskell, REXX, Delphi, XSLT, Tcl, Ruby).
Исходный вариант ASP от Microsoft использовал возможности ActiveScripting для выполнения работы. Шаблон ASP превращался в программу на VBScript или JScript, содержащую HTML для вывода и логику страницы. Этот код скармливался движку ActiveScripting и компилировался им для реального выполнения. Движок ActiveScripting имел "объекты", манипулированием которых, программа могла делать что-то полезное. Самым очевидным был объект
Response
, но были и другие - вроде Session
и т.п. Программа запускалась и рендерила страницу, возвращаемую клиенту.Страницы WebSnap (по крайней мере, использующие
TPageProducer
) использовали подобный же подход для генерации HTML страниц. Проблема разработчиков Delphi заключалась в том, как связать произвольные объекты Delphi с движком ActiveScripting, который использует позднее связывание через COM-ский IDispatch
для связи. Интерфейс IDispatch
- один из главных фундаментов COM в Windows, использует единственный метод (Invoke
) для вызовов любых методов. Вот где на сцену выходит $METHODINFO
- богатое описание методов информацией RTTI позволяет выполнить вызов всего в одной точке (Invoke
) для вызова произвольных методов Delphi.Скрипты VBScript или JavaScript, запущенные в скриптовой части страницы WebSnap, нуждаются в возможности общения с объектами Delphi (вроде
Page
и Session
), и для реализации этой возможности Delphi использует расширенный RTTI. Вы можете увидеть объекты WebSnap, предоставляемые скриптам - посмотрите в WebSnapObjs.pas
объекты TResponseObj
, TProducerObj
и т.д.Модуль
ObjAuto
содержит код и заголовок для извлечения RTTI информации, используя такую функцию:
function GetMethodInfo(Instance: TObject; const MethodName: ShortString): PMethodInfoHeader;В свою очередь, базовый класс
TScriptableObject
(помеченный $METHODINFO
) использует эту RTTI информацию для поиска методов и их вызова в run-time.ObjAuto.pas
Этот модуль содержит код для поиска RTTI метода. Посмотрев наGetMethodInfo
, вы увидите, что он использует vmtMethodTable
(объявленное в System.pas
) для получения доступа к таблице методов класса. Затем он ищет метод в этой таблице. Модуль также содержит код, который может сделать произвольный вызов, построив передачу параметров по RTTI метода и переходом к коду метода:
function ObjectInvoke(Instance: TObject; MethodHeader: PMethodInfoHeader; const ParamIndexes: array of Integer; const Params: array of Variant): Variant;Как вы можете видеть, вы можете передать параметры в виде вариантов (
variant
), а функция преобразует их в нужный тип, упакует их в регистры и/или стек и сделает вызов. Исходный код этой функции показывает всю сложность этой задачи с учётом различных моделей вызова. Вот как объекты на VBScript могут вызывать объекты Delphi внутри WebSnap.DetailedRTTI.pas
Пока я игрался с мета-данными, я создал несколько вспомогательных helper классов для помощи в разборе этой информации. Вы можете скачать код, если захотите с этого начать. Простой вызов.RTTIMethodsAsString()
для любого объекта, чтобы получить список его методов и параметров. Код немного грубоват, но вы вольны использовать его как угодно.Итоги
Эта и предыдущая статьи показывают возможности расширенной RTTI информации для методов, доступную в Delphi, и рассказывает о вспомогательных подпрограммах для доступа к ней. Мета-данные интерфейсов позволяют VCL поддерживать SOAP, где множество методов соединены в одну точку вызова. Мета-данные классов позволяют VCL поддерживать WebSnap, где также множество методов соединены в одну точку вызова - позволяя автоматически прозрачно проецировать COM-йIDispatch
на методы Delphi объектов.
Комментариев нет:
Отправить комментарий
Можно использовать некоторые HTML-теги, например:
<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>
Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.
Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.
Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.
Примечание. Отправлять комментарии могут только участники этого блога.