Search code examples
pythondelphidllpchar

get PChar argument back from Delphi DLL in my Python script


Let say that I've this function in a DLL

function test_3(sInput, sOutput : PChar; sSize : int64): Integer; stdcall;
var
  sTmp : string;
  fText : TextFile;
begin
  sTmp := '+++ ' + sInput + ' +++';
  StrPLCopy(sOutput, PChar(sTmp), sSize);
  Result := 69;
  AssignFile(fText, 'test.txt');
  Rewrite(fText);
  Writeln(fText, 'in: ' + sInput);
  Writeln(fText, 'out: '  + sOutput);
  CloseFile(fText);
end;

In my Delphi program, I call it like this

…
  Input := EdtIn.Text;
  OutputSize := Input.Length + 8;
  Output := AllocMem(OutputSize);
  RC := test_3(PChar(Input), Output, OutputSize);
  EdtOut.Text := Output;
  FreeMem(Output);

and it works quite well. Now I want to call the function from a Python script.

  import ctypes as ct
  ...
  myString = "test Delphi 10.3 DLL"
  outputsize = len(myString) + 8
  …
  test_3 = lib.test_3
  test_3.restype = ct.c_int
  test_3.argtypes = [ct.c_wchar_p, ct.c_wchar_p]
  sOutput = ct.create_string_buffer(outputsize)
  print("sOutput = " + sOutput.value)

and I get an error

ctypes.ArgumentError: argument 2: : wrong type

So my question is: what is the Python equivalent of AllocMem in Delphi. I must precise that, of course all the code is for example and in "real life" I've no access to the Delphi code in DLL.


Solution

  • Here is a simple and complete example demonstrating how to do this:

    Delphi library

    library SO_60391682;
    
    uses
      SysUtils;
    
    function testStringOut(Input, Output: PChar; OutputLen: Int64): Integer; stdcall;
    var
      tmp: string;
    begin
      tmp := '+++ ' + Input + ' +++';
      StrPLCopy(Output, PChar(tmp), OutputLen - 1); 
      // -1 because of StrPLCopy's handling of null terminator
      Result := 0;
    end;
    
    exports
      testStringOut;
    
    begin
    end.
    

    Python program to call the Delphi library

    import ctypes
    
    lib = ctypes.WinDLL(r'SO_60391682.dll')
    testStringOut = lib.testStringOut
    testStringOut.restype = ctypes.c_int
    testStringOut.argtypes = ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_int64
    
    output = ctypes.create_unicode_buffer(256)
    res = testStringOut('foo', output, len(output))
    print('res={}, output={}'.format(res, output.value))