Ricevere valori di javascript complessi attraverso un'interfaccia esterna
-
06-09-2019 - |
Domanda
Sto cercando di ricevere e inviare i valori potenzialmente complessi attraverso TWebBrowser (usando TEmbeddedWB) con l'oggetto esterno fornito. Per esempio; in javascript cercherei di utilizzare il metodo esposto con un array come parametro:
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);
Verifica della VarType di entrambi questi esempi mi dice che è un IDispatch.
function TSomeClass.someFunction(var Param : OleVariant) : OleVariant;
var
vType : Integer;
begin
vType := (VarType(Param) and VarTypeMask); //Says 9 (varDispatch)
Result := true;
end;
Io non sono completamente familiarità con COM e non sono sicuro di come lavorare con questo.
Qualsiasi aiuto sarebbe apprezzato.
Soluzione
Si può trattare l'oggetto JScript come qualsiasi altro oggetto COM OleVariant. Ci sono alcuni trucchi in termini di matrici (e praticamente qualsiasi oggetto JScript è essenzialmente un array sparso).
Dopo avere ottenuto l'oggetto JScript in un OleVariant si può semplicemente chiamare come si farebbe con qualsiasi codice normale (senza tempo di compilazione controllare ovviamente).
Un po 'di codice per trattare con gli array:
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;
Altri suggerimenti
Anche se non ho fatto direttamente quello che si sta cercando.
con una variante che si può effettivamente i metodi di accesso e le proprietà in modo dinamico.
In pratica ho il sospetto che si dovrebbe essere in grado di accedere a tutto direttamente.
Param.Someproperty
Param.SomeArray[1]
Param.SomeMethod();
Non si ottiene errori di compilazione di tempo se si sbaglia quindi state attenti.
Ad esempio il seguente codice compila, ma darà un errore di esecuzione di un'operazione variante non valida, non c'è niente assegnato dinamicamente a quella variabile.
var
vo : OleVariant;
v : Variant;
begin
v.DoThis;
vo.DoThat;
end;
Avete considerato la serializzazione dei dati complessi utilizzando JavaScript Object Notation (JSON)? Ciò consentirebbe di serializzare oggetti arbitrari JavaScript, li passa come una semplice stringa di caratteri e li ricostituire nel codice Delphi.
Delphi 2009 ha il supporto per JSON come parte del nuovo DataSnap (non so come sia facile da usare stand-alone). Ci sono anche una serie di implementazioni Delphi JSON là fuori che potrebbero rivelarsi utili:
Non sono un esperto in JSON, ma sembra essere una soluzione semplice ed efficiente relativo per lo scambio di dati cross-language.
David
Oggetti in Javascript sono array associativi , con nomi di proprietà di essere chiavi: obj.prop
è equivalente a obj['prop']
array regolari sono oggetti semplicemente memorizzare indici come proprietà, quindi si comportano come array sparsi.
OleVariants di Delphi consentono l'accesso diretto alle proprietà, ma solo quando i loro nomi sono validi Delphi identificatori , in modo che non piace utilizzando un indice numerico come un nome di proprietà (cioè obj.0
non compila).
Proprietà con i nomi di identificatori non validi possono essere letti DISPATCH_PROPERTYGET
invocando come nel di Ryan risposta .
Tuttavia Delphi includono le routine appropriate in ComObj
unit da fare direttamente in questo modo:
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;