Search code examples
delphivariantiactivescript

Delphi pass string parameter to IDispatch.Invoke


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

function test2(n)
  MsgBox n
  test2 = n
end function

VBScript code loads OK, passing an integer as parameter works OK, but when passing a string parameter, I found only the first half of the string makes it to the script. Delphi code:

procedure TForm1.Button4Click(Sender: TObject);
var
  Disp: IDispatch;
  Res: OleVariant;
  DispParams: TDispParams;
  s: string;
  n: Integer;
  v: Variant;
  Arg: array of TVariantArg;
begin
  OleCheck(FScript.GetScriptDispatch(nil, Disp));

  s := 'test2';

  OleCheck(Disp.GetIDsOfNames(GUID_NULL, @s, 1, 1033, @n));

  v := VarArrayOf(['1234567890']);

  SetLength(Arg, VarArrayHighBound(v, 1) - VarArrayLowBound(v, 1) + 1);

  arg[0].vt := VT_BSTR;
  arg[0].bstrVal := PWideChar(VarToStr(v[0])); //passes first half of string only
//  arg[0].bstrVal := SysAllocString(PWideChar(VarToStr(v[0]))); //passes complete (copy of) string
  end;

  DispParams.rgvarg := @Arg[0];
  DispParams.rgdispidNamedArgs := nil;
  DispParams.cArgs := 1;
  DispParams.cNamedArgs := 0;
//at this point, debugger shows no difference, bstrVal holds full string
  OleCheck(Disp.Invoke(n, GUID_NULL, 1033, DISPATCH_METHOD, DispParams, @res, nil, nil));
end;

MsgBox shows 12345. Tried other strings, other string lengths, too, always first half only.

Anyone who can shine a light on this?


Solution

  • When interfacing with COM, you need to use WideString (a wrapper for a COM BSTR string) instead of using string (aka UnicodeString), eg:

    procedure TForm1.Button4Click(Sender: TObject);
    var
      Disp: IDispatch;
      Res: OleVariant;
      DispParams: TDispParams;
      s: WideString;
      n: Integer;
      v: Variant;
      Arg: array of TVariantArg;
    begin
      OleCheck(FScript.GetScriptDispatch(nil, Disp));
    
      s := 'test2';
      OleCheck(Disp.GetIDsOfNames(GUID_NULL, @s, 1, 1033, @n));
    
      v := VarArrayOf(['1234567890']);
    
      SetLength(Arg, VarArrayHighBound(v, 1) - VarArrayLowBound(v, 1) + 1);
    
      s := VarToWideStr(v[0]);
      arg[0].vt := VT_BSTR;
      arg[0].bstrVal := PWideChar(s);
      //arg[0].bstrVal := SysAllocString(PWideChar(s));
    
      DispParams.rgvarg := @Arg[0];
      DispParams.rgdispidNamedArgs := nil;
      DispParams.cArgs := 1;
      DispParams.cNamedArgs := 0;
    
      OleCheck(Disp.Invoke(n, GUID_NULL, 1033, DISPATCH_METHOD, DispParams, @res, nil, nil));
    
      //SysFreeString(arg[0].bstrVal);
    end;