How do I call a delphi function that takes and ret

2019-08-02 19:55发布

问题:


This question is similar to How to access with ctypes to functions returning custom types coded in a Delphi dll?. Where I think this one is different is that the delphi function signature that I'm looking at takes pointers as arguments instead of delphi types.

It's also similar to Python pass Pointer to Delphi function except as mentioned in the comments, that question lacks necessary information.


How do I call a delphi function that takes and returns pointers of custom type from Python?

I'm calling delphi functions by loading a DLL with ctypes in Python.

>>> from ctypes import *
>>> path = "C:\\test.dll"
>>> lib = windll.LoadLibrary(path)
>>> lib.myFunc
<_FuncPtr object at 0x21324905> 

The delphi function "myFunc" has a signature like:

Procedure myFunc(Ptr:Pointer;Var Result:Pointer);Export;

Where both pointers should be custom data types, e.g.

Type
  PSingleArray = ^SingleArray;
  SingleArray = Record
    LowIndex : SmallInt;
    HighIndex : SmallInt;
    Data : Array [0..1000] of Single;
  end;

Going through the ctypes tutorial and the docs it seems like the way to solve this is to use "Structure" to create similar types in Python. I think I'm doing it properly; however, when I go to call myFunc an Access Violation Error is raised.

>>> class SingleArray(Structure):
>>>    _fields_ = [("HighIndex", c_int), ("LowIndex", c_int), ("Data", c_int * 10)]
>>> ...
>>>  lib.myFunc.argtypes = [POINTER(SingleArray)]
>>>  lib.myFunc.restype = POINTER(SingleArray)

>>>  # initialize the input values
>>>  input = SingleArray()
>>>  a = c_int * 10
>>>  data = a(1,2,3,4,5,6,7,8,9,10)
>>>  input.Data = data
>>>  input.HighIndex = 2
>>>  input.LowIndex = 1
>>>  # Here comes the access violation error
>>>  ret = lib.myFunc(input)
WindowsError: exception: access violation reading 0x00000002

I'm new to both ctypes and delphi, so I could be missing something obvious.

回答1:

I can see the following simple problems:

  • Delphi Smallint is a signed 16 bit type. That matches up to c_short.
  • Delphi Single is an IEEE-754 floating point value. That matches up to c_float.
  • The Delphi type has an array of length 1001. Yours has length 10.
  • The Delphi function has two parameters and no return value. Your argtypes and restype assignments do not match.
  • Your ctypes code uses stdcall calling convention but the Delphi function would appear to use the Delphi specific register calling convention. This makes the function impossible to call from anything other than another Delphi module.
  • It's not obvious who owns the memory returned via the Result parameter, nor how to deallocate it.

More significantly, it seems likely that the array in the structure is of variable length. That will be quite tricky to marshal with ctypes. You certainly cannot do so using Structure._fields_.

If you can change the Delphi DLL, do so. In its current form it is essentially unusable from ctypes. Even from Delphi code it is appalling to use.

If you cannot change the Delphi DLL then I think you'll need to write an adapter DLL, in Delphi or FPC, that presents a more sane interface for your Python code to work with.