通过外部接口接收复杂的 JavaScript 值
-
06-09-2019 - |
题
我正在尝试使用提供的外部对象通过 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对象,就像任何其它OleVariant 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;
其他提示
虽然我没有直接做了你你想什么。
具有一个变体就可以就实际访问的方法和属性动态。
基本上,我怀疑你应该能够直接访问一切。
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的OleVariants允许属性的直接访问,但只有当他们的名字是有效的德尔福标识符,所以它不会像使用数字索引作为属性名称(即obj.0
不编译)。
具有无效标识符名称属性可被读取调用DISPATCH_PROPERTYGET
如 Ryan的响应。
然而的Delphi包括在ComObj
unit适当例程直接做到这一点:
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;