Получение сложных значений javascript через внешний интерфейс

StackOverflow https://stackoverflow.com/questions/923736

Вопрос

Я пытаюсь получать и потенциально отправлять сложные значения через 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;
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top