Search code examples
delphivariantiactivescript

Delphi pass multiple params to IActiveScript IDispatch.Invoke


Using Delphi 11.1, I want to add scripting to my application using IActiveScript. I created a small VBScript to test passing multiple parameters from Delphi to the Script:

Function TestParams(a, b, c)
  TestParams = c
End Function

VB script load OK, but I have trouble passing multiple params. Delphi code:

    procedure TForm1.Button14Click(Sender: TObject);
    var
      v: OleVariant;
      Disp: IDispatch;
      Arg: TArray<TVariantArg>;
      Res: OleVariant;
      DispParams: TDispParams;
      i,n: Integer;
      s: string;
    begin
      v := VarArrayOf(['Wrong...', 'huh', 'OK!']);
      s := 'TestParams';
    
      Memo2.Lines.Text := VarToStr(MyScriptingHost1.Run('TestParams', v));
    exit;
      OleCheck(MyScriptingHost1.FScript.GetScriptDispatch(nil, Disp));
      OleCheck(Disp.GetIDsOfNames(GUID_NULL, @s, 1, 1033, @n));
    
      setlength(arg, 3);

      for i := 0 to High(Arg) do
      begin
        n := High(Arg) - i;
    
        Arg[n].vt := VarType(v[i]);
        Arg[n].bstrVal := PWideChar(VarToWideStr(v[i]));
      end;
   //At this point, my Delphi 11.1 assignes the same value to Arg[]0, Arg[1], arg[2] 
   
//this works
    //Arg[0].vt := VT_BSTR;
    //Arg[0].bstrVal := 'test3';
    //
    //Arg[1].vt := VT_BSTR;
    //Arg[1].bstrVal := 'test2';
    //
    //Arg[2].vt := VT_BSTR;
    //Arg[2].bstrVal := 'test1';
    
      DispParams.rgvarg := @Arg[0]; //@Arg gives error
      DispParams.rgdispidNamedArgs := nil;
      DispParams.cArgs := High(Arg) + 1;
      DispParams.cNamedArgs := 0;
    
    //passing pointer to DispParams gives errors
      OleCheck(Disp.Invoke(n, GUID_NULL, 1033, DISPATCH_METHOD, DispParams, @res, nil, nil));
    end;

For some reason, multiple params gives different results for 32/64 bits, and using the code above, All params get usually the same value. Very strange.

Even more strange, running this several times gives sometimes different results.

The above code works without problems in case of only 1 param.

Anyone who knows what is wrong here?


Solution

  • As I explained in reply to your earlier question, you MUST use WideString when interfacing with COM, not string (ie, when calling Disp.GetIDsOfNames()).

    Also, your use of VarToWideStr() is producing temporary WideStrings that are no longer valid by the time you pass the Arg array to Disp.Invoke(), so store the WideStrings in another array to keep them in scope until Invoke() exits.

    Try this:

    procedure TForm1.Button14Click(Sender: TObject);
    var
      v: OleVariant;
      Disp: IDispatch;
      Arg: TArray<TVariantArg>;
      ArgStrs: TArray<WideString>;
      Res: OleVariant;
      DispParams: TDispParams;
      i, n: Integer;
      s: WideString;
    begin
      v := VarArrayOf(['Wrong...', 'huh', 'OK!']);
      s := 'TestParams';
    
      //Memo2.Lines.Text := VarToStr(MyScriptingHost1.Run('TestParams', v));
    
      OleCheck(MyScriptingHost1.FScript.GetScriptDispatch(nil, Disp));
      OleCheck(Disp.GetIDsOfNames(GUID_NULL, @s, 1, 1033, @n));
    
      SetLength(Arg, 3);
      SetLength(ArgStrs, 3);
    
      for i := 0 to High(Arg) do
      begin
        ArgStrs[i] := VarToWideStr(v[i]);
        n := High(Arg) - i;
        Arg[n].vt := VT_BSTR;
        Arg[n].bstrVal := PWideChar(ArgStrs[i]);
      end;
    
      DispParams.rgvarg := @Arg[0]; //@Arg gives error
      DispParams.rgdispidNamedArgs := nil;
      DispParams.cArgs := Length(Arg);
      DispParams.cNamedArgs := 0;
    
      OleCheck(Disp.Invoke(n, GUID_NULL, 1033, DISPATCH_METHOD, DispParams, @res, nil, nil));
    end;