Добрый день!
Поводом для этой шпаргалки стали мои мои попытки разобраться с работой MS Script Control.
Для начала оговорюсь сразу в этой заметке не будет детального описания работы для этого уже есть много материала в Интернете, например: "Использование Microsoft ScriptControl" ,
Здесь же пойдет речь о самой работе с Script Control, так сказать некоторые ньюансы его использования, предпологается что читатель уже имеет некоторое представление:
Поводом для этой шпаргалки стали мои мои попытки разобраться с работой MS Script Control.
Для начала оговорюсь сразу в этой заметке не будет детального описания работы для этого уже есть много материала в Интернете, например: "Использование Microsoft ScriptControl" ,
Здесь же пойдет речь о самой работе с Script Control, так сказать некоторые ньюансы его использования, предпологается что читатель уже имеет некоторое представление:
Создание объекта
Для того, что-бы создать объект и присоеденить его к Microsoft Script нужно реализовать класс поддерживающий интерфейс IDispatch. Т.к у нас все таки шпаргалка, я привожу класс-протитип для реализации интерфейса IDispatch.
TBase = class(TInterfacedObject, IDispatch) private Protected 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; virtual; stdcall; Abstract; { DispID - содержит диспетчерский идентификатор (dispatch ID), это число, которое показывает какой метод должен быть вызван в сервере. IID - на данный момент не используется LocaleID - содержит информацию о языке. Flags - определяет какого типа метод будет вызван в сервере: метод доступа к свойствам или обычный метод. Params - содержит указатель на массив TDispParams, который содержит параметры, передаваемые этому методу. VarResult- это указатель на переменную типа OleVariant, в которую будет записано возвращаемое значение вызываемого метода. ExcepInfo- указатель на запись типа TExceptInfo, которая будет содержать информацию об ошибке, в случае, если метод Invoke() возвращает значение DISP_E_TYPEMISMATCH или DISP_E_PARAMNOTFOUND. ArgErr - указатель на целое число (индекс некорректного параметра в массиве Params). } function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer) : HResult; virtual; stdcall; Abstract; public function CheckArgCount(Count: Integer; Accepted: Integer; ExcepInfo : Pointer) : Boolean; function IsValidType(Argument: TVariantArg; TypesId : Variant): Boolean; function IsVdalidTypeBSTR(Argument: TVariantArg): Boolean; procedure VariantChangeTypeToBSTR(var Argument: TVariantArg); procedure SetErrorCheckParamsType(ExcepInfo : Pointer; MethodName : string = ''); procedure SetErrorInfo(ExcepInfo : Pointer; Description : String; MethodName : string = ''); end;
В данный класс помимо реализации интерфейса IDispatch включены некоторые вспомогательные функции, такие как проверка типа, проверка кол-ва аргументов в процедуре или функции, генерация информации о ошибке для Script Control
Далее наследуя от этого класса мы и будем реализовывать собственный объект я назвал его MyClass который в последствии мы подключим к Script Control
Итак:
Далее наследуя от этого класса мы и будем реализовывать собственный объект я назвал его MyClass который в последствии мы подключим к Script Control
Итак:
type TMyClass = class(TBase) Private function GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; override; stdcall; function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; override; stdcall; public end;
Перекрывая GetIDsOfNames и Invoke реализуем собственную функциональность.
Далее для того что бы зарегистрировать данный объект в Script Control вызовем ScriptControl.AddObject
ScriptControl.Language := 'VBScript'; ScriptControl.UseSafeSubset := False; ScriptControl.AllowUI := true; ScriptControl.Reset; ScriptControl.AddObject('MYCLASS', TMyClass.Create as IDispatch , FALSE);
Создание процедуры
Итак класс мы создали, теперь в данном классе нужно создать различные методы которые в будущем мы сможем вызывать со скрипта, т.к. самы простой метод это процедура (не возвращает никаких данных) то начнем с нее, для начала определимся в нашем классе будут следующие методы MESSAGEINFORMATION и GETRANDOMNUMBER. Первый будет выдавать диалоговое окно с сообщением которое будет посылатся в параметре, второй для тестового примера возращать случайное число.Итак в начале создадим константы, и сделаем реализацию метода GetIDsOfNames
const DISPID_MessageInformation = 1; fn_MESSAGEINFORMATION = 'MESSAGEINFORMATION'; DISPID_GetNumber = 2; fn_GETNUMBER = 'GETRANDOMNUMBER'; // Метод GetIDsOfNames должен вернуть идентификаторы (DispID) для метода и // именованных аргументов метода. В нашем случае в задачу метода входит определить // обращение к имени и вернуть код ошибки для всех остальных имен: function TMyClass.GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; type TDispIDsArray = array[0..0] of TDISPID; PDispIDsArray = ^TDispIDsArray; var IDs: PDispIDsArray absolute DispIDs; i: integer; Name: WideString; begin Result := S_OK; // Не поддерживаем именованные аргументы if NameCount > 1 then Result := DISP_E_UNKNOWNNAME else if NameCount < 1 then Result := E_INVALIDARG else Result := S_OK; for i := 0 to NameCount - 1 do IDs[i] := DISPID_UNKNOWN; if NameCount = 1 then begin Name := PWideChar(Names^); IDs[0] := 0; if UpperCase(Name) = fn_MESSAGEINFORMATION then IDs[0] := DISPID_MessageInformation; if UpperCase(Name) = fn_GERANDOMTNUMBER then IDs[0] := DISPID_GetRandomNumber; if IDs[0] = 0 then Result := DISP_E_UNKNOWNNAME; end; end;
После чего сделаем реализаци метода Invoke, где и пропишем обработку:
function TMyClass.Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; var P: TDISPPARAMS absolute Params; ByIntRef1 : Integer; Err : HRESULT; ErrMethod : TErrMethodInfo; Str : String; begin if DispId = 0 then begin OleVariant(VarResult^) := Self as IDispatch; Result := S_OK; Exit; end; case DispID of DISPID_MessageInformation : begin // Проверка на кол-во параметров if not CheckArgCount(P.cArgs, 1, ExcepInfo) then begin Result := DISP_E_EXCEPTION; Exit; end; // Аргументы в обратном порядке if IsVdalidTypeBSTR(P.rgvarg^[0]) then begin ShowMessage('Информация ' + #10#13 + TDispParams(P).rgvarg^[0].bstrval); end else begin SetErrorCheckParamsType(ExcepInfo, fn_MESSAGEINFORMATION); Result := DISP_E_EXCEPTION; end; end; end; Result := S_OK; end;
Обратите внимание что пока реализован фактически только метод MESSAGEINFORMATION. Пока так и задумано, GETRANDOMNUMBER рассмотрим чуть позже.
Теперь сделаем VB-скрипт и запустим его с нашей программы. Маленькое примечание, т.к. при запуске Script Control требует имя функции которую нужно запустить в скрипте у нас она будет иметь имя main. Запуск скрипта будем производить так, текс скрипта берется из memo-поля:
procedure TForm1.BitBtn1Click(Sender: TObject); var P : PSafeArray; SA : TSafeArrayBound; begin ScriptControl.AddCode(Memo.Text); SA.cElements := 0; P := SafeArrayCreate(varVariant, 1, @SA); ScriptControl.Run('Main',P); end;
Сам скрипт для начала будет таким:
Sub Main()
MyCLASS.MessageInformation("Проверка работы")
End Sub
Запускаем проверяем, вроде работает
Но не спешим радоваться, не все так гладко, есть и подводные камни:
Продолжение следует....
Далее в следующих частях: Проверка типов параметров; Преобразование типов параметров
Комментариев нет:
Отправить комментарий