-1

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.

Jean-Philippe
  • 173
  • 10
  • Try using `ct.c_char_p` as the argtype. – Jeronimo Feb 25 '20 at 09:56
  • Is it possible to create dummy string of needed length and use it for the second argument? – MBo Feb 25 '20 at 09:58
  • 1
    Unicode Delphi, or ANSI Delphi? Python 2 or Python 3? – David Heffernan Feb 25 '20 at 09:58
  • Delphi 2007 and older = ANSI, Delphi 2009 and newer = Unicode! – Delphi Coder Feb 25 '20 at 10:01
  • @Jeronimo How can you say that? You can't know whether the Delphi `PChar` is 8 bit or 16 bit. – David Heffernan Feb 25 '20 at 10:02
  • @David Heffernan Because `ctypes.create_string_buffer` creates a `ctypes.c_char_Array`, which I thought was the direct cause for the "wrong type" error. – Jeronimo Feb 25 '20 at 10:19
  • @Jeronimo But that would be the wrong fix if `PChar` was an alias for `PWideChar` (which it is for Delphi >= 2009). We can't answer the question until the asker provides the missing information requested in my comment above. – David Heffernan Feb 25 '20 at 10:21
  • 1
    sorry, I've forgot to point out: Delphi 10.3 and Python 3.7 – Jean-Philippe Feb 25 '20 at 10:30
  • 1
    As you are using an Unicode Delphi version, your calculation for ``OutputSize`` (at least in the Delphi example, can't tell for Python) is wrong. AllocMem allocates memory in bytes, not in characters. So you need to double the value of ``OutputSize``, because a Unicode character is two bytes big. – Delphi Coder Feb 25 '20 at 10:40
  • and the input and output are ANSI string – Jean-Philippe Feb 25 '20 at 10:40
  • 1
    "and the input and output are ANSI string". Not true. Since you are using Delphi 10.3 `PChar` is an alias for `PWideChar`, pointer to null terminated array of UTF-16 character elements. And yes, your memory allocation is broken. I would like to answer the question, but won't bring myself to do so until this is cleared up. – David Heffernan Feb 25 '20 at 11:42
  • @David I have to ask my colleague who developed the "real life" DLL. I will see him tomorrow – Jean-Philippe Feb 25 '20 at 12:05
  • @David, my colleague confirms me that the input and output are Unicode and not Ansi as I originally think. – Jean-Philippe Feb 26 '20 at 07:40
  • @Delphi Coder, don't worry about the size of the output; the goal of the real DLL, developed by my colleague is to translate Edifact messages to Xml and JSon, so the output is set to 100 * size of the input. – Jean-Philippe Feb 26 '20 at 07:40
  • I'll have a look at this later. But rest assured that none of us care in the least about your memory allocations. You however should care about your erroneous allocations. – David Heffernan Feb 26 '20 at 07:44

1 Answers1

2

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))
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490