Использование Microsoft ScriptControl

Экземпляр этого класса создается при регистрации объекта в TScriptControl и уничтожается автоматически, когда потребность в нем исчезает. Поле FOwner хранит ссылку на экземпляр класса VCL, интерфейс к которому предоставляет объект, зарегистрированный в TScriptControl. TVCLScriptControl – это наследник TScriptControl. Главным его отличием является наличие списка зарегистрированных экземпляров TVCLProxy и обработчиков событий, позволяющих компонентам VCL вызывать методы скрипта. Пишем GetIdsOfNames В методе GetIdsOfNames мы должны проверить наличие запрошенного свойства и вернуть адрес его структуры TPropInfo, если такое свойство найдено. Свойства компонентов VCL

TVCLProxy.GetIDsOfNames( IID: TGUID; Names: Pointer;
  NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
  S: ;
  Info: PPropInfo;
  Result := S_OK;
  // Получаем имя функции или свойства
  S := PNamesArray(Names)[0];
  // Проверяем, есть ли VCL-свойство с таким же именем
  Info := GetPropInfo(FOwner.ClassInfo, S);
  Assigned(Info)
  // Свойство есть, возвращаем в качестве DispId
  // адрес структуры PropInfo
  PDispIdsArray(DispIds)[0] := Integer(Info);
Дополнительные функции Дополним нашу реализацию возможностью вызова некоторых дополнительных функций: Controls Для наследников TWinControl возвращает ссылку на дочерний компонент с именем или индексом, заданным в параметре. Count Для компонентов TWinControl – возвращает количество дочерних компонентов. Для TCollection – возвращает количество элементов. Для TStrings – возвращает количество строк. Add Для компонентов TWinControl – создает дочерний компонент. Для TCollection – добавляет элемент в коллекцию. Для TStrings – добавляет строку. HasProperty Возвращает истину, если у объекта есть свойство с заданным именем. Для этого дополним метод GetIdsOfNames следующим кодом:
// Нет такого свойства, проверяем, не имя ли это
  // одной из определенных нами функций
  CompareText(S, 'CONTROLS') = 0
  (FOwner TWinControl)
  PDispIdsArray(DispIds)[0] := DISPID_CONTROLS
  Result := DISP_E_UNKNOWNNAME;

  CompareText(S, 'COUNT') = 0
  (FOwner TCollection) (FOwner TStrings)
  (FOwner TWinControl)
  PDispIdsArray(DispIds)[0] := DISPID_COUNT
  Result := DISP_E_UNKNOWNNAME;
  CompareText(S, 'ADD') = 0
  Result := S_OK;
  (FOwner TCollection) (FOwner TStrings)
  (FOwner TWinControl)
  PDispIdsArray(DispIds)[0] := DISPID_ADD
  Result := DISP_E_UNKNOWNNAME;
  CompareText(S, 'HASPROPERTY') = 0
  PDispIdsArray(DispIds)[0] := DISPID_HASPROPERTY
  Result := DISP_E_UNKNOWNNAME;
 ;

Константы DISPID_CONTROLS, DISPID_COUNT и т.д. определены как целые числа из диапазона 1…1 000 000. Это вполне безопасно, поскольку адрес структуры TPropInfo никак не может оказаться менее 1 Мбайт.
Пишем Invoke
Первая часть задачи выполнена: мы проинформировали OLE о наличии в нашем сервере автоматизации поддерживаемых функций. Теперь необходимо реализовать метод Invoke для выполнения этих функций. Из соображений модульности Invoke выполняет подготовительную работу со списком параметров и вызывает метод DoInvoke, в котором мы осуществляем трансляцию DispID в обращения к методам класса VCL.
В методе используются три служебные функции: ·проверяет количество переданных аргументов. ·проверяет соответствие аргумента с заданным индексом заданному типу. ·получает целое число из аргумента с заданным индексом.
TVCLProxy.DoInvoke(DispID: Integer; IID: TGUID;
LocaleID: Integer; Flags: Word; dps: TDispParams;
pDispIds: PDispIdList; VarResult, ExcepInfo, ArgErr: Pointer
): HResult;
S: ;
Put: Boolean;
I: Integer;
P: TPersistent;
B: Boolean;
OutValue: OleVariant;
Result := S_OK;
DispId
Для функции Controls мы должны проверить, что передан один параметр. Если он строковый — поиск дочернего компонента будет происходить по имени, в противном случае — по индексу. Если компонент найден – вызывается функция FScriptControl.GetProxy, которая проверяет наличие «представителя» у этого компонента, при необходимости создает его и возвращает интерфейс IDispatch. Такой алгоритм необходим для корректной работы оператора VBScript Is, который сравнивает две ссылки на объект и выдает истину в случае, если речь идет об одном и том же объекте, например:
Dim A
Dim B
Set A = C
Set B = C
If A is B Then ...
Если создавать экземпляр класса TVCLProxy каждый раз, когда запрашивается ссылка, эти экземпляры окажутся разными и оператор Is не будет работать.

 DISPID_CONTROLS:

  // Вызвана функция Controls

  FOwner TWinControl

  // Проверяем параметр

  CheckArgCount(dps.cArgs, [1], TRUE);

  P := ;

  _ValidType(0, VT_BSTR, FALSE)

  // Если параметр - строка - ищем дочерний компонент

  // с таким именем

  S := dps.rgvarg^[pDispIds^[0]].bstrVal;

  I := 0 Pred(ControlCount)

  CompareText(S, Controls[I].Name) = 0

  P := Controls[I];

  Break;

  ;

  // Иначе - параметр - число, берем компонент по индексу

  I := _IntValue(0);

  P := Controls[I];

  ;

  Assigned(P)

  // Компонент не найден

  EInvalidParamType.Create('');

  // Возвращаем интерфейс IDispatch для найденного компонента

  OleVariant(VarResult^) := FScriptControl.GetProxy(P);

  ;

  ;

Функция Count должна вызываться без параметров и призвана возвращать количество элементов в запрашиваемом объекте.

 DISPID_COUNT:

  // Вызвана функция Count

  // Проверяем, что не было параметров

  CheckArgCount(dps.cArgs, [0], TRUE);

  FOwner TWinControl

  // Возвращаем количество дочерних компонентов

  OleVariant(VarResult^) := TWinControl(FOwner).ControlCount;

  FOwner TCollection

  // Возвращаем количество элементов коллекции

  OleVariant(VarResult^) := TCollection(FOwner).Count

  FOwner TStrings

  // Возвращаем количество строк

  OleVariant(VarResult^) := TStrings(FOwner).Count;

  ;

Метод Add добавляет элемент к объекту-владельцу «представителя». Обратите внимание на реализацию необязательных параметров для TWinControl и TStrings.

 DISPID_ADD:

  // Вызвана функция Add

  FOwner TWinControl

  // Проверяем количество аргументов

  CheckArgCount(dps.cArgs, [2,3], TRUE);

  // Проверяем типы обязательных аргументов

  _ValidType(0, VT_BSTR, TRUE);

  _ValidType(1, VT_BSTR, TRUE);

  // Третий аргумент - необязательный, если он не задан -

  // полагаем FALSE

  (dps.cArgs = 3) _ValidType(2, VT_BOOL, TRUE)

  B := dps.rgvarg^[pDispIds^[0]].vbool

  B := FALSE;

  // Вызываем метод для создания компонента

  DoCreateControl(dps.rgvarg^[pDispIds^[0]].bstrVal,

  dps.rgvarg^[pDispIds^[1]].bstrVal, B);



  FOwner TCollection

  // Добавляем компонент

  P := TCollection(FOwner).Add;

  // И возвращаем его интерфейс IDispatch

  OleVariant(varResult^) := FScriptControl.GetProxy(P);

  FOwner TStrings

  // Проверяем наличие аргументов

  CheckArgCount(dps.cArgs, [1,2], TRUE);

  // Проверяем, что аргумент – строка

  _ValidType(0, VT_BSTR, TRUE);

  dps.cArgs = 2 then

  // Второй аргумент - позиция в списке

  I := _IntValue(1)

  // Если его нет - вставляем в конец

  I := TStrings(FOwner).Count;

  // Добавляем строку

  TStrings(FOwner).Insert(I,

  dps.rgvarg^[pDispIds^[0]].bstrVal);

  ;

  ;

И наконец, функция HasProperty проверяет наличие у объекта VCL опубликованного свойства с заданным именем.

 DISPID_HASPROPERTY:

  // Вызвана функция HasProperty

  // Проверяем наличие аргумента

  CheckArgCount(dps.cArgs, [1], TRUE);

  // Проверяем тип аргумента

  _ValidType(0, VT_BSTR, TRUE);

  S := dps.rgvarg^[pDispIds^[0]].bstrVal;

  // Возвращаем True, если свойство есть

  OleVariant(varResult^) :=

  Assigned(GetPropInfo(FOwner.ClassInfo, S));

  ;

Если ни один из DispID не обработан — значит DispID содержит адрес структуры TPropInfo свойства VCL
 // Это не наша функция, значит это свойство

  // Проверяем Flags, чтобы узнать, устанавливается значение

  // или получается

  Put := (Flags DISPATCH_PROPERTYPUT) <> 0;

  Put

  // Устанавливаем значение

  // Проверяем наличие аргумента

  CheckArgCount(dps.cArgs, [1], TRUE);

  // И устанавливаем свойство

  Result := SetVCLProperty(PPropInfo(DispId),

  dps.rgvarg^[pDispIds^[0]])

  // Получаем значение

  DispId = 0

  // DispId = 0 - требуется свойство по умолчанию

  // Возвращаем свой IDispatch

  OleVariant(VarResult^) := Self IDispatch;

  Exit;

  ;

  // Получаем значение свойства

  Result := GetVCLProperty(PPropInfo(DispId),

  dps, pDispIds, OutValue);

  Result = S_OK

  // Получили успешно - сохраняем результат

  OleVariant(VarResult^) := OutValue;

  ;

  ;

 ;

Добавление собственных функций
Для добавления функций, которые требуются для решения ваших задач, необходимо выполнить ряд простых шагов: ·В методе GetIdsOfNames проанализировать имя запрашиваемой функции и определить, может ли она быть вызвана для объекта, на который ссылается FOwner. ·Если функция может быть вызвана, вы должны вернуть уникальный DispID, в противном случае – присвоить Result := DISP_E_UNKNOWNNAME. ·В методе Invoke необходимо обнаружить свой DispID, проверить корректность переданных параметров, получить их значения и выполнить действие.
Обработка событий в компонентах VCL
Важным дополнением к реализуемой функциональности является возможность ассоциировать процедуру на VBScript с событием в компоненте VCL, таким как OnEnter, OnClick или OnTimer. Для этого добавим в компонент TVCLScriptControl методы, которые будут служить обработчиками созданных в коде скрипта компонентов.

TVCLScriptControl = (TScriptControl)

  …

  OnChangeHandler(Sender: TObject);

  OnClickHandler(Sender: TObject);

  OnEnterHandler(Sender: TObject);

  OnExitHandler(Sender: TObject);

  OnTimerHandler(Sender: TObject);

  ;

Отправить комментарий

Проверка
Антиспам проверка
Image CAPTCHA
...