Получение сложных значений javascript через внешний интерфейс
-
06-09-2019 - |
Вопрос
Я пытаюсь получать и потенциально отправлять сложные значения через TWebBrowser (используя TEmbeddedWB) с предоставленным внешним объектом.Например;в javascript я бы попытался использовать открытый метод с массивом в качестве параметра:
var test = [123, 'abc'];
external.someFunction(test);
//Or something more complex
var complexObject = {
someMethod : function(){ return 1; },
someProperty : 123,
someArray : ['xyz', 3.14]
}
external.someFunction(complexObject);
Проверка VarType обоих этих примеров говорит мне, что это IDispatch.
function TSomeClass.someFunction(var Param : OleVariant) : OleVariant;
var
vType : Integer;
begin
vType := (VarType(Param) and VarTypeMask); //Says 9 (varDispatch)
Result := true;
end;
Я не совсем знаком с COM и не уверен, как с этим работать.
Будем признательны за любую помощь.
Решение
Вы можете обрабатывать объект JScript так же, как любой другой олевариантный COM-объект.Есть несколько подводных камней в терминах массивов (и практически любой объект JScript по сути является разреженным массивом).
После получения объекта JScript в OleVariant вы можете просто вызвать его, как любой обычный код (без проверки времени компиляции, конечно).
Вот некоторый код для работы с массивами:
type
TJScriptArray = class
private
FArray: IDispatchEx;
FCount: Integer;
function GetProperty( const AName: String ): OleVariant;
function GetItem(Index: Integer): OleVariant;
public
constructor Create( AObj: OleVariant );
destructor Destroy; override;
public
property Count: Integer read FCount;
property Item[Index: Integer]: OleVariant read GetItem; default;
end;
function VarToDispatchEx( const AObject: OleVariant ): IDispatchEx;
begin
Result := nil;
if VarType( AObject ) <> varDispatch then
Exit;
Supports( IDispatch(AObject), IDispatchEx, Result );
end;
function IsJScriptArray( const AObject: OleVariant ): Boolean;
var
temp: IDispatchEx;
begin
temp := VarToDispatchEx( AObject );
Result := temp <> nil;
end;
constructor TJScriptArray.Create(AObj: OleVariant);
begin
inherited Create;
FArray := VarToDispatchEx( AObj );
if FArray = nil then
raise Exception.Create( 'TJscriptArray called with invalid parameters.' );
FCount := GetProperty( 'length' );
end;
destructor TJScriptArray.Destroy;
begin
inherited Destroy;
end;
function TJScriptArray.GetItem(Index: Integer): OleVariant;
begin
if Index > FCount then
raise Exception.Create( 'Index out of bounds.' );
Result := GetProperty( IntToStr( Index ) );
end;
function TJScriptArray.GetProperty(const AName: String): OleVariant;
var
sz: WideString;
id: Integer;
res: Variant;
ei: TExcepInfo;
params: TDispParams;
hr: HResult;
begin
{
ACTION: return the specified property from the jscript array
NOTE: since a jscript array is a sparse array there may be
gaps. In that case a null variant is returned. This is
signalled by the name (id) not existing.
}
sz := AName;
hr := FArray.GetDispID( PWideChar(sz), 0, id );
if hr = disp_e_UnknownName then begin
Result := Null;
Exit;
end
else
OleCheck( hr );
VarClear( res );
FillChar( ei, sizeof(ei), 0 );
FillChar( params, sizeof(params), 0 );
OleCheck( FArray.InvokeEx( id, 0, dispatch_PropertyGet, @params, @res, @ei, nil ) );
Result := res;
end;
Другие советы
Хотя я напрямую не делал того, что вы пытаетесь.
с помощью Variant вы можете фактически получать доступ к методам и свойствам динамически.
В принципе, я подозреваю, что вы должны иметь прямой доступ ко всему.
Param.Someproperty
Param.SomeArray[1]
Param.SomeMethod();
Вы не получите ошибок во время компиляции, если что-то сделаете неправильно, так что будьте осторожны.
Например, следующий код компилируется, но выдает ошибку времени выполнения из-за недопустимой операции с вариантом, поскольку этой переменной ничего не присваивается динамически.
var
vo : OleVariant;
v : Variant;
begin
v.DoThis;
vo.DoThat;
end;
Рассматривали ли вы возможность сериализации ваших сложных данных с использованием объектной нотации JavaScript (JSON)?Это позволило бы вам сериализовать произвольные объекты JavaScript, передать их в виде простой строки символов и воссоздать их в коде Delphi.
Delphi 2009 поддерживает JSON как часть нового DataSnap (не уверен, насколько легко использовать его автономно).Существует также ряд реализаций Delphi JSON, которые могут оказаться полезными:
Оформить покупку lkjson и JSON - СуперОбъект
Я не эксперт в JSON, но это кажется относительно простым и эффективным решением для межъязыкового обмена данными.
Дэвид
Объекты в Javascript являются ассоциативными массивами, причем имена свойств являются ключами: obj.prop
эквивалентно obj['prop']
.
Обычные массивы - это просто объекты, хранящие индексы как свойства, поэтому они ведут себя как разреженные массивы.
Олеварианты Delphi разрешают прямой доступ к свойствам, но только тогда, когда их имена допустимы Идентификаторы Delphi, поэтому ему не нравится использовать числовой индекс в качестве имени свойства (т. е. obj.0
не компилируется).
Свойства с недопустимыми именами идентификаторов могут быть прочитаны с помощью вызова DISPATCH_PROPERTYGET
как в Ответ Райана.
Однако Delphi включает соответствующие процедуры в ComObj
устройство для непосредственного выполнения этого:
uses ComObj;
...
function TSomeClass.someFunction(var Param : OleVariant) : OleVariant;
begin
ShowMessage(Param.someProperty); // 123
ShowMessage(GetDispatchPropValue(Param, 'someProperty')); // 123
ShowMessage(Param.someArray.length); // 2
ShowMessage(GetDispatchPropValue(Param.someArray, '0')); // xyz
Result := true;
end;