Использование Microsoft ScriptControl
Интеграция TScriptControl с VCL
В существующем виде возможности TScriptControl сильно ограничены сложным доступом к классам VCL. Исполнение интерпретируемого кода – это хорошо, однако хотелось бы иметь возможность их него обращаться к компонентам в программе, получать и устанавливать их свойства, обрабатывать возникающие в них события, например следующим образом:
Code: |
Sub Main() Dim Control Control = Self.Controls("Panel2") Control.Add "Panel3", "TPanel" With Panel3 .Align = "alTop" .BevelOuter = "bvNone" .Height = 40 .Caption = "" .Add "Btn", "TButton", True With Btn .Top = 10 .Left = .Top .Caption = "Click me" End With
End With End Sub
Sub Btn_OnClick() Dim StatusBar Dim Panel Dim I I = 0 For Each Panel In StatusBar.Panels I = I + 1 With Panel .Text = .Text & " " & CStr(I) End With Next End Sub |
Дальнейшая часть главы посвящена реализации такой функциональности, однако, прежде чем приступить к этому, необходимо более подробно рассмотреть некоторые механизмы, лежащие в основе модели расширения TScriptControl и VCL
Модель расширения ScriptControl
Как уже было рассмотрено выше, Microsoft ScriptControl позволяет сделать доступными из скрипта объекты, реализованные в программе при помощи метода AddObject. При обращении к таким объектам он предполагает, что они реализуют интерфейс IDispatch и являются, таким образом, OLE-automation серверами. В Delphi в качестве таких объектов могут выступать наследники TAutoObject, создать которых можно при помощи мастера, вызываемого из меню File -> New -> ActiveX -> Automation Object. При вызове методов этих объектов ScriptControl последовательно вызывает методы GetIdsOfNames и Invoke их интерфейса IDispatch, что приводит к вызовам соответствующих методов объекта. Однако здесь имеются определенные сложности:
1. | По окончании работы с объектом (например, при выходе его за пределы области видимости процедуры скрипта) TScriptControl автоматически вызывает его метод _Release, что приведет к уничтожению класса Delphi. Таким образом, для каждого класса приходится создавать некий объект-представитель, который бы транслировал вызовы TScriptControl в методы и свойства класса Delphi, а при исчезновении необходимости – уничтожался, не уничтожая самого класса |
2. | Функциональность наследников TAutoObject задается на этапе компиляции и не может быть расширена в процессе исполнения программы. Это заставляет создавать отдельных представителей для каждого класса VCL, что очень сложно в реализации и не позволяет использовать классы, для которых нет соответствующего представителя. |
Чтобы понять пути обхода этой проблемы необходимо более детально вникнуть в реализацию базового интерфейса, лежащего в основе автоматизации OLE
Интерфейс IDispatch
Интерфейс IDispatch обеспечивает возможность позднего связывания, т.е. вызовов методов объектов не по адресам, а по именам на этапе выполнения программы. Интерфейс определен как:
Code: |
type IDispatch = interface(IUnknown) ['{00020400-0000-0000-C000-000000000046}'] function GetTypeInfoCount(out Count: Integer): Integer; stdcall; function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): Integer; stdcall; function GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount, LocaleID: Integer; DispIDs: Pointer): Integer; stdcall; function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): Integer; stdcall; end; |
Ключевыми методами интерфейса являются GetIdsOfNames и Invoke.
function GetIdsOfNames
Этот метод осуществляет трансляцию имен методов и свойств объекта автоматизации в целочисленные идентификаторы. Если OLE пытается разрешить ссылку вида:
SomeObject.DoSomeThing
Она запрашивает у SomeObject интерфейс IDispatch и вызывает метод GetIdsOfNames, передавая ему ссылку на массив имен требующих разрешения в параметре Names, количество имен в параметре NameCount и региональный контекст в параметре LocaleId. Метод должен заполнить массив, на который указывает параметр DispIds значениями идентификаторов имен. Объект имеет возможность предоставить разные имена методов для каждого поддерживаемого языка. Если это не нужно – Вы можете игнорировать параметр LocaleId.
Стандартная реализация IDispatch ищет информацию об именах методов и их идентификаторах в библиотеке типов объекта, однако, программист вполне может взять эту работу на себя и осуществлять самостоятельную трансляцию.
Просьба писать ваши замечания, наблюдения и все остальное,
что поможет улучшить предоставляемую информацию на этом сайте.
ВСЕ КОММЕНТАРИИ МОДЕРИРУЮТСЯ ВРУЧНУЮ, ТАК ЧТО СПАМИТЬ БЕСПОЛЕЗНО!