views:

634

answers:

3

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.

+2  A: 

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;
Robert Love
You're right, methods and properties are accessible this way. I haven't had luck with the arrays though. I haven't tried selecting an individual element like SomeArray[1], which may work, but I can't get VarArrayHighBound(SomeArray, 1) or low bound to work on it. It doesn't think it is an array or something. This is when I just pass an array by itself instead of making the array a property of an object. Do you have any experience with this?
Tim
+1  A: 

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

David Taylor
+2  A: 

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;
Ryan VanIderstine