Recevoir des valeurs javascript complexes à travers l'interface externe
-
06-09-2019 - |
Question
Je suis en train de recevoir et envoyer potentiellement des valeurs complexes par TWebBrowser (en utilisant TEmbeddedWB) avec l'objet externe fourni. Par exemple; javascript Je voudrais essayer d'utiliser la méthode exposée avec un tableau comme paramètre:
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);
Vérification de la VarType de ces deux exemples me dit qu'il est un IDispatch.
function TSomeClass.someFunction(var Param : OleVariant) : OleVariant;
var
vType : Integer;
begin
vType := (VarType(Param) and VarTypeMask); //Says 9 (varDispatch)
Result := true;
end;
Je ne suis pas tout à fait familier avec COM et je ne suis pas sûr de savoir comment travailler avec cela.
Toute aide serait appréciée.
La solution
Vous pouvez traiter l'objet JScript comme tout autre objet OleVariant COM. Il y a quelques gotchas en termes de tableaux (et à peu près tout objet JScript est essentiellement un tableau creux).
Après avoir obtenu l'objet JScript dans un OleVariant vous pouvez simplement l'appeler comme vous le feriez pour tout code normal (sans compilation de vérification bien sûr).
Voici un code pour traiter les tableaux:
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;
Autres conseils
Bien que je n'ai pas directement fait ce que vous vous essayez.
avec une variante, vous pouvez vous en fait des méthodes d'accès et propriétés dynamiquement.
En fait, je pense que vous devriez être en mesure d'accéder à tout directement.
Param.Someproperty
Param.SomeArray[1]
Param.SomeMethod();
Vous n'obtiendrez des erreurs de compilation de temps si vous avez des choses mal alors faites attention.
Par exemple, le code suivant compile, mais donnera une erreur d'exécution de l'opération de variante non valide car il n'y a rien dynamiquement assigné à cette variable.
var
vo : OleVariant;
v : Variant;
begin
v.DoThis;
vo.DoThat;
end;
Avez-vous envisagé sérialisation vos données complexes en utilisant JavaScript Object Notation (JSON)? Cela vous permettra de sérialiser des objets JavaScript arbitraires, les passer comme une simple chaîne de caractères et les réhydrater dans le code Delphi.
Delphi 2009 a le soutien de JSON dans le cadre de la nouvelle DataSnap (ne sais pas comment il est facile à utiliser autonome). Il y a aussi un certain nombre d'implémentations Delphi JSON là-bas qui pourrait se révéler utile:
Je ne suis pas expert en JSON, mais il semble être une solution simple et efficace relative pour l'échange de données de langue croix.
David
Objets Javascript sont des tableaux associatifs , avec des noms de propriété étant clés: obj.prop
est équivalent à obj['prop']
tableaux réguliers sont des objets simplement stocker des index comme des propriétés, de sorte qu'ils se comportent comme des tableaux rares.
Les OleVariants Delphi permettent un accès direct aux propriétés, mais seulement lorsque leurs noms sont valides Delphi identifiants , donc il n'aime pas utiliser un index numérique comme un nom de propriété (c.-à-obj.0
ne compile pas).
Propriétés avec des noms d'identification non valides peuvent être lus comme dans l'invocation DISPATCH_PROPERTYGET
réponse de Ryan .
Cependant Delphi comprennent des routines propres à ComObj
unit à faire directement ceci:
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;