Question

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:

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.

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.

Any help would be appreciated.

Was it helpful?

Solution

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).

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).

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;

OTHER TIPS

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

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

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;

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.

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:

Checkout lkjson and JSON - SuperObject

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

David

Objects in Javascript are associative arrays, with property names being keys: obj.prop is equivalent to 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).

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

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

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;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top