Recepción de valores de JavaScript complejos a través de la interfaz externa
-
06-09-2019 - |
Pregunta
Estoy tratando de recibir y enviar valores potencialmente complejas a través de TWebBrowser (usando TEmbeddedWB) con el objeto externo proporcionado. Por ejemplo; en javascript me gustaría tratar de utilizar el método expuesto con una matriz como un parámetro:
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);
Comprobación del VarType de estos dos ejemplos me dice que es un IDispatch.
function TSomeClass.someFunction(var Param : OleVariant) : OleVariant;
var
vType : Integer;
begin
vType := (VarType(Param) and VarTypeMask); //Says 9 (varDispatch)
Result := true;
end;
No estoy completamente familiarizado con COM y no estoy seguro de cómo trabajar con esto.
Cualquier ayuda sería apreciada.
Solución
Puede tratar el objeto JScript al igual que cualquier otro objeto COM OleVariant. Hay unos pocos aspectos críticos en términos de arrays (y casi cualquier objeto JScript es esencialmente una matriz dispersa).
Después de conseguir el objeto JScript en un OleVariant simplemente puede llamar como lo haría con cualquier código normal (sin tiempo de compilación, por supuesto).
Aquí hay un código para hacer frente a las matrices:
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;
Otros consejos
A pesar de que no lo han hecho directamente lo que usted está tratando.
con una variante que se puede realmente métodos de acceso y propiedades de forma dinámica.
Básicamente sospecho que debe ser capaz de acceder a todo directamente.
Param.Someproperty
Param.SomeArray[1]
Param.SomeMethod();
Usted no recibirá errores de compilación de tiempo si usted consigue las cosas mal, así que tenga cuidado.
Por ejemplo el siguiente código compila, pero dará un error de ejecución de la operación variante no válida como no hay nada asignado dinámicamente a esa variable.
var
vo : OleVariant;
v : Variant;
begin
v.DoThis;
vo.DoThat;
end;
¿Ha considerado la serialización de los datos complejos usando JavaScript Object Notation (JSON)? Esto le permitiría a serializar objetos JavaScript arbitrario, pasar como una simple cadena de caracteres y lo reconstituye en código de Delphi.
Delphi 2009 tiene soporte para JSON como parte de la nueva DataSnap (no estoy seguro de lo fácil que es utilizar independiente). También hay una serie de implementaciones Delphi JSON por ahí que podrían resultar útiles:
No soy experto en JSON, pero parece ser una solución sencilla y eficaz relativa para el intercambio de datos entre diferentes idiomas.
David
Los objetos en Javascript son matrices asociativas , con los nombres de propiedades siendo claves: obj.prop
es equivalente a obj['prop']
matrices regulares son simplemente objetos de almacenamiento de índices como las propiedades, por lo que se comportan como matrices dispersas.
OleVariants de Delphi permiten el acceso directo a las propiedades, pero sólo cuando sus nombres son válidos Delphi identificadores , por lo que no le gusta el uso de un índice numérico como un nombre de propiedad (es decir obj.0
no compila).
Las propiedades con nombres de identificadores no válidos pueden ser leídos DISPATCH_PROPERTYGET
invocando como en de Ryan respuesta .
Sin embargo Delphi incluye rutinas adecuadas en ComObj
unit que ver directamente lo siguiente:
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;