Reputation: 193
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.
Upvotes: 2
Views: 2400
Reputation: 3401
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 ComObj
unit 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;
Upvotes: 0
Reputation: 481
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;
Upvotes: 3
Reputation: 2051
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
Upvotes: 1
Reputation: 12581
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;
Upvotes: 2