InfoCity
InfoCity - виртуальный город компьютерной документации
Реклама на сайте







Размещение сквозной ссылки

 

Оператор For Each


Удобным средством, предоставляемым VBScript, является оператор For Each, организующий цикл по всем элементам заданной коллекции. Добавим поддержку этого оператора в наш компонент.

Интерфейс IEnumVariant


Реализация For Each предусматривает следующее:

  • Исполняющее ядро ScriptControl вызывает метод Invoke объекта, по элементам которого должен производиться цикл с DispID = DISPID_NEWENUM (-4).
  • Объект должен вернуть интерфейс IenumVariant.
  • Далее ядро использует методы IEnumVariant для получения элементов коллекции.

Интерфейс IEnumVariant определен как:

  IEnumVariant = (IUnknown)
     ['{00020404-0000-0000-C000-000000000046}']
      Next(celt: LongWord;  rgvar: OleVariant;
       pceltFetched: PLongWord): HResult; ;
      Skip(celt: LongWord): HResult; ;
      Reset: HResult; ;
     Clone(out Enum: IEnumVariant): HResult; ;
   ;  

В модуле ActiveX.pas в оригинальной поставке Delphi5 ошибочно определен метод Next

   Next(celt: LongWord;  rgvar: OleVariant;
       pceltFetched: LongWord): HResult; ;  

поэтому для корректной реализации интерфейс должен быть переопределен.

Класс TVCLEnumerator


Создадим класс, инкапсулирующий функциональность IEnumVariant.

  TVCLEnumerator = (TInterfacedObject, IEnumVariant)
   
     FEnumPosition: Integer;
     FOwner: TPersistent;
     FScriptControl: TVCLScriptControl;
     { IEnumVariant }
      Next(celt: LongWord;  rgvar: OleVariant;
       pceltFetched: PLongWord): HResult; ;
     Skip(celt: LongWord): HResult; ;
      Reset: HResult; ;
     Clone(Enum: IEnumVariant): HResult; ;
   
     Create(AOwner: TPersistent;
       AScriptControl: TVCLScriptControl);
   ;  

Конструктор устанавливает свойства FOwner и FScriptControl.

 TVCLEnumerator.Create(AOwner: TPersistent;
   AScriptControl: TVCLScriptControl);
 
   Create;
   FOwner := AOwner;
   FScriptControl := AScriptControl;
   FEnumPosition := 0;
 ;  

Метод Reset подготавливает реализацию интерфейса к началу перебора.

 TVCLEnumerator.Reset: HResult;
 
   FEnumPosition := 0;
   Result := S_OK;
 ;  

Главная функциональность сосредоточена в методе Next, который получает следующие переменные:

  • celt – количество запрашиваемых элементов;
  • rgvar – адрес первого элемента массива переменных типа OleVariant;
  • pceltFetched – адрес переменной, в которую должно быть записано количество реально переданных элементов. Этот адрес может быть равен NIL, в этом случае не потребуется ничего записывать.

Метод должен заполнить запрошенное количество элементов rgvar и вернуть S_OK, если это удалось, и S_FALSE, если элементов не хватило.

  TVariantList = [0..0] OleVariant;
 
   
 TVCLEnumerator.Next(celt: LongWord;  rgvar: OleVariant;
   pceltFetched: PLongWord): HResult;
 
   I: Cardinal;
 
   Result := S_OK;
   I := 0;  

Для объекта TWinControl возвращаем интерфейсы IDispatch для компонентов из свойства Controls.

   FOwner  TWinControl 
      TWinControl(FOwner) 
        (FEnumPosition < ControlCount)  (I < celt) 
         TVariantList(rgvar)[I] :=
           FScriptControl.GetProxy(Controls[FEnumPosition]);
         Inc(I);
         Inc(FEnumPosition);
       ;
     ;

Для TCollection организуется перебор элементов коллекции.

   FOwner TCollection 
     TCollection(FOwner) 
       (FEnumPosition < Count) (I < celt) 
         TVariantList(rgvar)[I] :=
           FScriptControl.GetProxy(Items[FEnumPosition]);
         Inc(I);
         Inc(FEnumPosition);
       ;
     ; 

Для TStrings перебираются строки и возвращаются их значения.

    FOwner TStrings 
     TStrings(FOwner) 
       (FEnumPosition < Count) (I < celt) 
         TVariantList(rgvar)[I] := TStrings(FOwner)[FEnumPosition];
         Inc(I);
         Inc(FEnumPosition);
       ;
     ;
   
     Result := S_FALSE;
    I <> celt 
     Result := S_FALSE;
    Assigned(pceltFetched) 
     pceltFetched^ := I;
 ;  

Метод Skip пропускает запрошенное количество элементов и возвращает S_OK, если еще остались элементы для перебора.

TVCLEnumerator.Skip(celt: LongWord): HResult;
 
   Total: Integer;
 
   Result := S_FALSE;
    FOwner  TWinControl 
     Total := TWinControl(FOwner).ControlCount
   
    FOwner  TCollection 
     Total := TCollection(FOwner).Count
   
    FOwner  TStrings 
     Total := TStrings(FOwner).Count
   
     Exit;
    FEnumPosition + celt <= Total 
     Result := S_OK;
     Inc(FEnumPosition, celt)
   ;
 ;  

Метод Clone клонирует объект, возвращая интерфейс его копии.

TVCLEnumerator.Clone( Enum: IEnumVariant): HResult;
 
   NewEnum: TVCLEnumerator;
 
   NewEnum := TVCLEnumerator.Create(FOwner, FScriptControl);
   NewEnum.FEnumPosition := FEnumPosition;
   Enum := NewEnum  IEnumVariant;
   Result := S_OK;
 ;  

Для того чтобы класс TVCLProxy мог вернуть интерфейс IEnumVariant, требуется дополнить метод Invoke следующим кодом:

  DispId 
    DISPID_NEWENUM: 
         // У объекта запрашивают интерфейс IEnumVariant для ForEach
         // создаем класс, реализующий этот интерфейс
         OleVariant(VarResult^) := TVCLEnumerator.Create(FOwner,
           FScriptControl)  IEnumVariant;
       ;  

Компонент TVCLScriptControl


Текст этого компонента приведен на CD-ROM. Данный компонент является наследником TScriptControl и реализует функциональность по работе с TVCLProxy.

Заключение


Microsoft ScriptControl – качественное решение для задач, требующих включения в программу интерпретирующего ядра. Интегрировав его с VCL, мы получаем мощный и гибкий инструмент, позволяющий наращивать возможности в любом направлении. Приведенной в данной статье информации вполне достаточно, чтобы на основе помещенного на компакт-диске компонента TVCLScriptControl создать решение, удовлетворяющее любой конкретной задаче.

[Назад]


Реклама на InfoCity

Яндекс цитирования



Финансы: форекс для тебя








1999-2009 © InfoCity.kiev.ua