通过外部接口接收复杂的javascript值

时间:2022-03-21 17:37:46

I'm trying to receive and potentially send complex values through TWebBrowser (using TEmbeddedWB) with the provided external object. For example; in javascript I would try to use the exposed method with an array as a parameter:

我正在尝试通过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);

Checking the VarType of both of these examples tells me it's a IDispatch.

检查这两个示例的VarType告诉我它是一个IDispatch。

function TSomeClass.someFunction(var Param : OleVariant) : OleVariant;
var
  vType : Integer;
begin
  vType := (VarType(Param) and VarTypeMask); //Says 9 (varDispatch)
  Result := true;
end;

I'm not completely familiar with COM and I'm not sure how to work with this.

我对COM并不完全熟悉,我不知道如何使用它。

Any help would be appreciated.

任何帮助,将不胜感激。

4 个解决方案

#1


You can treat the JScript object just as any other OleVariant COM object. There are a few gotchas in terms of arrays (and just about any JScript object is essentially a sparse array).

您可以像处理任何其他OleVariant COM对象一样处理JScript对象。在数组方面存在一些问题(几乎任何JScript对象本质上都是稀疏数组)。

After getting the JScript object into an OleVariant you can simply call it as you would any normal code (without compile time checking of course).

将JScript对象转换为OleVariant之后,您可以像调用任何普通代码一样调用它(当然不需要编译时检查)。

Here is some code for dealing with arrays:

这是一些处理数组的代码:

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;

#2


Although I have not directly done what you you are trying.

虽然我没有直接做你正在尝试的事情。

with a Variant you can you actually Access methods and properties dynamically.

使用Variant,您可以动态地访问方法和属性。

Basically I suspect you should be able to access everything directly.

基本上我怀疑你应该能够直接访问所有内容。

Param.Someproperty
Param.SomeArray[1]
Param.SomeMethod();

You will not get compile time errors if you get things wrong so be careful.

如果出错,你不会得到编译时错误,所以要小心。

For example the following code compiles, but will give a runtime error of invalid variant operation as there is nothing dynamically assigned to that variable.

例如,以下代码编译,但会给出无效变量操作的运行时错误,因为没有动态分配给该变量。

var
 vo : OleVariant;
 v  : Variant;
begin
  v.DoThis;
  vo.DoThat;
end;

#3


Have you considered serializing your complex data using JavaScript Object Notation (JSON)? This would allow you to serialize arbitrary JavaScript objects, pass them as a simple string of characters and reconstitute them in Delphi code.

您是否考虑过使用JavaScript Object Notation(JSON)序列化复杂数据?这将允许您序列化任意JavaScript对象,将它们作为一个简单的字符串传递,并在Delphi代码中重新构建它们。

Delphi 2009 has support for JSON as part of the new DataSnap (not sure how easy it is to use standalone). There are also a number of Delphi JSON implementations out there that might prove useful:

Delphi 2009支持JSON作为新DataSnap的一部分(不确定单独使用它是多么容易)。还有许多可能证明有用的Delphi JSON实现:

Checkout lkjson and JSON - SuperObject

结帐lkjson和JSON - SuperObject

I am no expert in JSON, but it seems to be a relative simple and efficient solution for cross-language data interchange.

我不是JSON的专家,但它似乎是跨语言数据交换的一种相对简单而有效的解决方案。

David

#4


Objects in Javascript are associative arrays, with property names being keys: obj.prop is equivalent to obj['prop'].

Javascript中的对象是关联数组,属性名称是键:obj.prop等同于obj ['prop']。

Regular arrays are simply objects storing indexes as properties, so they behave like sparse arrays.

常规数组只是将索引存储为属性的对象,因此它们的行为类似于稀疏数组。

Delphi's OleVariants allow direct access to properties, but only when their names are valid Delphi identifiers, so it doesn't like using a numeric index as a property name (i.e. obj.0 doesn't compile).

Delphi的OleVariants允许直接访问属性,但仅当它们的名称是有效的Delphi标识符时,所以它不喜欢使用数字索引作为属性名称(即obj.0不编译)。

Properties with invalid identifier names can be read invoking DISPATCH_PROPERTYGET as in Ryan's response.

可以读取具有无效标识符名称的属性,如Ryan的响应中那样调用DISPATCH_PROPERTYGET。

However Delphi include proper routines in ComObjunit to directly do this:

但是,Delphi在ComObjunit中包含适当的例程来直接执行此操作:

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;

#1


You can treat the JScript object just as any other OleVariant COM object. There are a few gotchas in terms of arrays (and just about any JScript object is essentially a sparse array).

您可以像处理任何其他OleVariant COM对象一样处理JScript对象。在数组方面存在一些问题(几乎任何JScript对象本质上都是稀疏数组)。

After getting the JScript object into an OleVariant you can simply call it as you would any normal code (without compile time checking of course).

将JScript对象转换为OleVariant之后,您可以像调用任何普通代码一样调用它(当然不需要编译时检查)。

Here is some code for dealing with arrays:

这是一些处理数组的代码:

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;

#2


Although I have not directly done what you you are trying.

虽然我没有直接做你正在尝试的事情。

with a Variant you can you actually Access methods and properties dynamically.

使用Variant,您可以动态地访问方法和属性。

Basically I suspect you should be able to access everything directly.

基本上我怀疑你应该能够直接访问所有内容。

Param.Someproperty
Param.SomeArray[1]
Param.SomeMethod();

You will not get compile time errors if you get things wrong so be careful.

如果出错,你不会得到编译时错误,所以要小心。

For example the following code compiles, but will give a runtime error of invalid variant operation as there is nothing dynamically assigned to that variable.

例如,以下代码编译,但会给出无效变量操作的运行时错误,因为没有动态分配给该变量。

var
 vo : OleVariant;
 v  : Variant;
begin
  v.DoThis;
  vo.DoThat;
end;

#3


Have you considered serializing your complex data using JavaScript Object Notation (JSON)? This would allow you to serialize arbitrary JavaScript objects, pass them as a simple string of characters and reconstitute them in Delphi code.

您是否考虑过使用JavaScript Object Notation(JSON)序列化复杂数据?这将允许您序列化任意JavaScript对象,将它们作为一个简单的字符串传递,并在Delphi代码中重新构建它们。

Delphi 2009 has support for JSON as part of the new DataSnap (not sure how easy it is to use standalone). There are also a number of Delphi JSON implementations out there that might prove useful:

Delphi 2009支持JSON作为新DataSnap的一部分(不确定单独使用它是多么容易)。还有许多可能证明有用的Delphi JSON实现:

Checkout lkjson and JSON - SuperObject

结帐lkjson和JSON - SuperObject

I am no expert in JSON, but it seems to be a relative simple and efficient solution for cross-language data interchange.

我不是JSON的专家,但它似乎是跨语言数据交换的一种相对简单而有效的解决方案。

David

#4


Objects in Javascript are associative arrays, with property names being keys: obj.prop is equivalent to obj['prop'].

Javascript中的对象是关联数组,属性名称是键:obj.prop等同于obj ['prop']。

Regular arrays are simply objects storing indexes as properties, so they behave like sparse arrays.

常规数组只是将索引存储为属性的对象,因此它们的行为类似于稀疏数组。

Delphi's OleVariants allow direct access to properties, but only when their names are valid Delphi identifiers, so it doesn't like using a numeric index as a property name (i.e. obj.0 doesn't compile).

Delphi的OleVariants允许直接访问属性,但仅当它们的名称是有效的Delphi标识符时,所以它不喜欢使用数字索引作为属性名称(即obj.0不编译)。

Properties with invalid identifier names can be read invoking DISPATCH_PROPERTYGET as in Ryan's response.

可以读取具有无效标识符名称的属性,如Ryan的响应中那样调用DISPATCH_PROPERTYGET。

However Delphi include proper routines in ComObjunit to directly do this:

但是,Delphi在ComObjunit中包含适当的例程来直接执行此操作:

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;