I receive this error when I try to call this function written in Delphi. But another code works fine. Maybe I'm not declaring the args ans result types? I am using 32Bit python 3.7). Related code snippets:
Delphi:
Test(deposit, MarginCall: double; CallBack: TProgrCallBackProc); stdcall;
Python:
self.FTCore = ctypes.WinDLL(self.FTCore_library_path)
self.FTCore.Test.argtypes = [ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_double)]
self.FTCore.Test.restype = ctypes.POINTER(ctypes.c_char)
deposit = ctypes.c_double(100)
callback = ctypes.c_double(1)
self.FTCore.Test(deposit, callback)
Error:
violation reading 0x00000004
Three errors that I can see:
The Delphi function accepts three arguments, you define only two in argtypes. You will need to define the third argument, TProgrCallBackProc defined somewhere in the Delphi code.
The two double parameters are passed by value, but you define them as pointers to double in your argtypes definition. They should be defined as plain ctypes.c_double.
The Delphi function has no return value, but your restype contradicts that. You need to set restype to None.
Related
I am new to programming, I defined a function in c++ with tuple method for two returned variables, after I compiled the files, In the python file, I try to access the two returned variables inside the dynamic library which have been compiled, but it is not working, there is segmentation error happened when I tried to run the python program. But I actually success with single return variable from c++, I think there is might be special trick for accessing the two returned variable with tuple method from python.
The following is the c++ code with two returned variables with tuple method
std::tuple<double, double> Cassie2d::Step(ControllerTorque* action)
{
dyn_model_.setState(mj_data_->qpos, mj_data_->qvel);
dyn_state_.UpdateDynamicState(&dyn_model_);
mju_copy(mj_data_->ctrl, action->torques, nU);
mj_step(mj_model_, mj_data_);
return std::make_tuple(mj_data_->qacc,mj_data_->time);
Render();
}
The following is the python method I was applied, due to both of the returned variables are double type.
lib.StepTorque.argtypes = [ctypes.c_void_p, ctypes.POINTER(ControllerTorque)]
lib.StepTorque.restype = ctypes.c_double
I guess the restype is not just equal to ctypes.c_double, because it worked for one returned variable and it might not work for two returned variables.
Really appreciate for the help!
With return std::make_tuple(mj_data_->qacc,mj_data_->time) your creating a structure which holds two variables, defined in parenthesis. If the function would be call from native C++ code you should be able to unpack it using std:tie, eq:
double a, b;
std::tie(a,b) = Step(arg);
However you need to call it directly from python, which has totally different syntax for returning multiple variables (C++ pair or tuple is kind of bypass missing functionality). I see two option in python.
Give your C++ function a two callbacks to python, and send variables separately this way:
void Cassie2d::Step(ControllerTorque* action, callback1, callback2)
{
dyn_model_.setState(mj_data_->qpos, mj_data_->qvel);
dyn_state_.UpdateDynamicState(&dyn_model_);
mju_copy(mj_data_->ctrl, action->torques, nU);
mj_step(mj_model_, mj_data_);
callback1(mj_data_->qacc);
callback2(mj_data_->time);
Render();
}
(Recommened) You can try to use native python option to serve multiple return. Left you C++ function in its initial form and call in python this way:
VarA, VarB = Step(action)
I assume you have a reference to Cassie2d::Step(ControllerTorque* action) method in your python code, however you provided very small snippet.
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.
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.
Here's the scenario: I am using ctypes to load a C DLL into a Python program. I want to register a callback so that code in the DLL can call a function in Python.
This is all great until I want to have the function be variadic. For example, say I wanted to implement "printf" in Python (for argument's sake, I'll also have it return an integer):
def printf(format, *args):
(...)
return 0
I am creating the callback function pointer using the CFUNCTYPE factory in ctypes:
callback_type = CFUNCTYPE(c_int, c_wchar_p)
Of course, since I only specify one input argument (the format string as a Unicode char array), the callback works but *args is empty.
So, is there any way of making this work with ctypes, or do I need to rewrite my C code to not require a variadic callback function?
No, it's not supported by the underlying libffi. You need indeed to write custom C code.
(Fwiw, CFFI is a ctypes alternative. Also based on libffi, it suffers from the same limitation --- but at least it makes it easy to write custom pieces of C code to do the interfacing.)
I am trying to call functions from a DLL which seems to be created in Delphi. An example of a some functions supported by the DLL are:
function oziDeleteWpByName(var name:pansichar):integer;stdcall
The Python code I have written to access the above functions is not working.
from ctypes import *
libc = cdll.OziAPI
name ='test'
pi = pointer(name)
delname = libc.oziDeleteWpByName
delname(name)
It seems I am passing the wrong data type to the function. Any ideas on how to do it right?
Thanks it worked. Now please help with this function:
function oziGetOziVersion(var Version:pansichar;var DataLength:integer):integer;stdcall;
The version of OziExplorer is returned in the Version variable.
Now how do I pass 'var version' when it the one which will also be returned.
from ctypes import *
# Not strictly needed but it's good to be explicit.
windll.OziAPI.oziDeleteWpByName.argtypes = [POINTER(c_char_p)]
windll.OziAPI.oziDeleteWpByName.restype = c_int
p = c_char_p('test')
retval = windll.OziAPI.oziDeleteWpByName(byref(p))
In Delphi, a var parameter is passed by reference. So what you have there is a pointer to a PAnsiChar (aka C-style string pointer). If you're passing it a string pointer, instead of a pointer to a string pointer, it won't work.
I wrote a test dll in C++ to make sure things work before I start using a more important dll that I need. Basically it takes two doubles and adds them, then returns the result. I've been playing around and with other test functions I've gotten returns to work, I just can't pass an argument due to errors.
My code is:
import ctypes
import string
nDLL = ctypes.WinDLL('test.dll')
func = nDLL['haloshg_add']
func.restype = ctypes.c_double
func.argtypes = (ctypes.c_double,ctypes.c_double)
print(func(5.0,5.0))
It returns the error for the line that called "func":
ValueError: Procedure probably called with too many arguments (8 bytes in excess)
What am I doing wrong? Thanks.
You probably got the calling conventions mixed up. I'm guessing you have a C function declared something like this:
double haloshg_add(double d1, double s2)
{
return d1+d2;
}
This will use the C calling convention by default. The simplest approach would be to change the calling convention in your ctypes code:
nDLL = ctypes.CDLL('test.dll')
If you wanted to change the calling convention in the C code to stdcall (to match ctypes.WinDLL) then you would do this:
double __stdcall haloshg_add(double d1, double s2)
Whatever you do, only do one of these changes. If you do both you'll have the reverse failure!
If it were me, I'd just change the Python code to use C calling convention (use CDLL). That change has the least impact.