I'm trying to execute my Python Code that must call a C function, which execute some calculation and save the value in a pointer that must be accessible from Python code. I'd like to do that because i'm constructing a DLL and i want to validate the algebra inside the DLL function, therefore i'd like to use a python code to validate the DLL.
The Python Code
from ctypes import *
if __name__ == '__main__':
mydll = cdll.LoadLibrary("./dll_simples.dll")
funcao = mydll.simuser
funcao.argtypes = c_double,c_double,POINTER(c_double),POINTER(c_double)
a = 0
b = 0
input_1 = (c_double * 1)()
input_1[0] = 5
output_1 = (c_double * 1)()
funcao(a,b,input_1,output_1)
and my DLL
__declspec(dllexport) void simuser(double t, double delt, double* in, double* out)
{
out[0] = 2 * in[0];
}
after executing this code, i have the error
funcao(a,b,input_1,output_1)
OSError: exception: access violation reading 0x0000000000000018
Listing [Python 3.Docs]: ctypes - A foreign function library for Python.
So, you want to pass an array to a function that expects a pointer. For that case, ctypes.cast is required:
So, instead of:
funcao(a, b, input_1, output_1)
use:
funcao(a, b, cast(input_1, POINTER(c_double)), cast(output_1, POINTER(c_double)))
Looking at the existing C code, it only uses one value for the 2 pointers, case in which you won't be needing arrays at all (but I doubt that's the intent because then the input value shouldn't be a pointer):
# ...
input_1 = c_double(5)
output_1 = c_double(0)
funcao(a, b, byref(input_1), byref(output_1))
A working example: [SO]: Pointer from Python (ctypes) to C to save function output (#CristiFati's answer).
Related
I am trying to get a response from a dll library in python using the ctypes wrapper. The response of the function should return x and y coordinates as c_short. A relevant snippet of my code is as follows:
ethdll = ctypes.cdll.LoadLibrary('C:\\Users\\jgallacher\\Documents\\Software_Drivers\\RTC4eth V2 Software Release 2021-06-25\\DLL\\RTC4ethDLLx64.dll')
def get_xy_pos(ethdll):
x = ctypes.c_short()
y = ctypes.c_short()
res = ethdll.get_xy_pos(ctypes.byref(x), ctypes.byref(y))
print(res)
However, when I try this definition I get a Nonetype(0) as the return. Can anyone suggest what is wrong with my function call? I've attached the
I've attached a picture showing the response type to this question thread.
Thanks!
Jordan.
The function returns void, so capturing the return value does nothing. You have created x and y variables to hold the result and need to inspect them after calling the function. Here's a working example with a sample DLL function implementation:
test.c
__declspec(dllexport)
void get_xy_pos(short *xpos, short *ypos) {
*xpos = 5;
*ypos = 7;
}
test.py
import ctypes as ct
dll = ct.CDLL('./test')
# Good practice is to define .argtypes and .restype so ctypes can do type-checking
dll.get_xy_pos.argtypes = ct.POINTER(ct.c_short),ct.POINTER(ct.c_short)
dll.get_xy_pos.restype = None
def get_xy_pos():
x = ct.c_short() # storage for output parameters
y = ct.c_short()
dll.get_xy_pos(ct.byref(x), ct.byref(y)) # pass by reference
return x.value,y.value # inspect the return values
print(get_xy_pos())
Output:
(5, 7)
I have a C function that reads a binary file and returns a dynamically sized array of unsigned integers (the size is based off metadata from the binary file):
//example.c
#include <stdio.h>
#include <stdlib.h>
__declspec(dllexport)unsigned int *read_data(char *filename, size_t* array_size){
FILE *f = fopen(filename, "rb");
fread(array_size, sizeof(size_t), 1, f);
unsigned int *array = (unsigned int *)malloc(*array_size * sizeof(unsigned int));
fread(array, sizeof(unsigned int), *array_size, f);
fclose(f);
return array;
}
This answer appears to be saying that the correct way to pass the created array from C to Python is something like this:
# example_wrap.py
from ctypes import *
import os
os.add_dll_directory(os.getcwd())
indexer_dll = CDLL("example.dll")
def read_data(filename):
filename = bytes(filename, 'utf-8')
size = c_size_t()
ptr = indexer_dll.read_data(filename, byref(size))
return ptr[:size]
However, when I run the python wrapper, the code silently fails at ptr[:size] as if I'm trying to access an array out of bounds, and I probably am, but what is the correct way to pass this dynamically size array?
A few considerations:
First, you need to properly set the prototype of the C function so that ctypes can properly convert between the C and Python types.
Second, since size is actually a ctypes.c_size_t object, you actually need to use size.value to access the numeric value of the array size.
Third, since ptr[:size.value] actually copies the array contents to a Python list, you'll want to make sure you also free() the allocated C array since you're not going to use it anymore.
(Perhaps copying the array to a Python list is not ideal here, but I'll assume it's ok here, since otherwise you have more complexity in handling the C array in Python.)
This should work:
from ctypes import *
import os
os.add_dll_directory(os.getcwd())
indexer_dll = CDLL("example.dll")
indexer_dll.read_data.argtypes = [c_char_p, POINTER(c_size_t)
indexer_dll.read_data.restype = POINTER(c_int)
libc = cdll.msvcrt
def read_data(filename):
filename = bytes(filename, 'utf-8')
size = c_size_t()
ptr = indexer_dll.read_data(filename, byref(size))
result = ptr[:size.value]
libc.free(ptr)
return result
I am currently working on a simultion-tool that requires a PDE Solver from a Fortran dll. In order to figure how calling a dll from python I used a simpler function from the same dll but can't get it to work.
Systemspecs:
Windows 7 Professional (64bit)
Spyder 3.2.8
Python 3.6.5 (32bit)
I am now using cffi to call the fortran function but it doesn't work either.
import cffi as cf
ffi=cf.FFI()
lib=ffi.dlopen("C:\Windows\SysWOW64\DLL20DDS")
ffi.cdef("""
double S01BAF(double X, int IFAIL);
""")
print (lib) #This works
print (lib.S01BAF) #This works
x = 1.
ifail = 0
print (lib.S01BAF(x,ifail)) #This doesn't work
This is the code i am using to call the function with cffi. The dll i am loading contains the function S01BAF which i intend to call.
The error message I recieve is:
runfile('C:/Users/Student/Desktop/Minimal.py', wdir='C:/Users/Student/Desktop')
<cffi.api._make_ffi_library.<locals>.FFILibrary object at 0x0759DB30>
<cdata 'double(*)(double, int)' 0x105BBE30>
Kernel died, restarting
I don't know what that means.
To check if the function itself is working I tryed calling it from a different language (VBA) and it worked just fine.
Option Base 1
Option Explicit
Private Declare Function S01BAF Lib "DLL20DDS.dll" (x As Double, iFail As Long) As Double
Sub ln()
Dim iFail As Long
Dim x As Double
Dim y As Double
x = 1
iFail = 0
y = S01BAF(x, iFail)
MsgBox y
End Sub
The messagebox displays the correct value for ln(2).
I have read the previously asked questions but couldn't apply the answers to my problem.
Here is the code that works thanks to #Joe!
ffi=cf.FFI()
lib=ffi.dlopen("C:\Windows\SysWOW64\DLL20DDS")
ffi.cdef("double S01BAF(double *x, int *ifail);")
x_1 = np.arange(-0.99,1,0.001)
x = ffi.new('double*', 1)
ifail = ffi.new('int*', 0)
y = (lib.S01BAF(x,ifail))
Cheers,
Thilo
The function definition of S01BAF
double s01baf_ (const double *x, Integer *ifail)
indicates that the variables x and ifail are pointers. Please try
x = cf.new('double*', 1.0)
ifail = cf.new("int*", 0)
lib.S01BAF(x, ifail)
or
x = cf.new('double*')
x[0] = 1.0
ifail = cf.new("int*")
ifail[0] = 0
lib.S01BAF(x, ifail)
I've asked this question before with no replies. I'm asking it again, much more simplified this time.
I have a dll called by Python ctypes, with a callback function. The callback works correctly all the way through (I can see it in operation if I step through the program in Visual Studio), but on exit Visual Studio throws an "access violation" exception. BUT if I remove the call to the callback from the dll, it exits normally without an access violation.
Is there something else I must do to exit from a dll with a callback? I have researched this for hours and I haven't found anything online that solves this.
Here's the ctypes code. I omitted the dll code to keep this short (it's written in NASM) but if it's needed I can post it, too.
def SimpleTestFunction_asm(X):
Input_Length_Array = []
Input_Length_Array.append(len(X)*8)
CA_X = (ctypes.c_double * len(X))(*X)
length_array_out = (ctypes.c_double * len(Input_Length_Array))(*Input_Length_Array)
hDLL = ctypes.WinDLL("C:/Test_Projects/SimpleTestFunction/SimpleTestFunction.dll")
CallName = hDLL.Main_Entry_fn
CallName.argtypes = [ctypes.POINTER(ctypes.c_double),ctypes.POINTER(ctypes.c_double),ctypes.POINTER(ctypes.c_longlong)]
CallName.restype = ctypes.POINTER(ctypes.c_int64)
#__________
#The callback function
LibraryCB = ctypes.WINFUNCTYPE(ctypes.c_double, ctypes.c_double)
def LibraryCall(ax):
bx = math.ceil(ax)
return (bx)
lib_call = LibraryCB(LibraryCall)
lib_call = ctypes.cast(lib_call,ctypes.POINTER(ctypes.c_longlong))
#__________
ret_ptr = CallName(CA_X,length_array_out,lib_call)
I would really REALLY appreciate any ideas on how to solve this. I hope this simplified post will help.
Thanks very much.
I made some minor changes to your code to make actually run (imports) and added a print to see the addresses of the objects passed and the return value, plus created an equivalent C DLL to ensure the pointers pass correctly and the callback works.
Python:
import ctypes
import math
def SimpleTestFunction_asm(X):
Input_Length_Array = []
Input_Length_Array.append(len(X)*8)
CA_X = (ctypes.c_double * len(X))(*X)
length_array_out = (ctypes.c_double * len(Input_Length_Array))(*Input_Length_Array)
hDLL = ctypes.WinDLL('test')
CallName = hDLL.Main_Entry_fn
CallName.argtypes = [ctypes.POINTER(ctypes.c_double),ctypes.POINTER(ctypes.c_double),ctypes.POINTER(ctypes.c_longlong)]
CallName.restype = ctypes.POINTER(ctypes.c_int64)
LibraryCB = ctypes.WINFUNCTYPE(ctypes.c_double, ctypes.c_double)
def LibraryCall(ax):
bx = math.ceil(ax)
return (bx)
lib_call = LibraryCB(LibraryCall)
lib_call = ctypes.cast(lib_call,ctypes.POINTER(ctypes.c_longlong))
ret_ptr = CallName(CA_X,length_array_out,lib_call)
print('{:016X} {:016X} {:016X} {}'.format(ctypes.addressof(CA_X),ctypes.addressof(length_array_out),ctypes.addressof(lib_call.contents),ret_ptr.contents))
SimpleTestFunction_asm([1.1,2.2,3.3])
Test.DLL source:
#include <inttypes.h>
#include <stdio.h>
typedef double (*CB)(double);
__declspec(dllexport) int64_t* __stdcall Main_Entry_fn(double* p1, double* p2, long long* p3)
{
static int64_t x = 123;
double out = ((CB)p3)(1.1);
printf("%p %p %p %lf\n",p1,p2,p3,out);
return &x;
}
Output:
0000021CC99B23A8 0000021CCBADAC10 0000021CCBC90FC0 2.000000
0000021CC99B23A8 0000021CCBADAC10 0000021CCBC90FC0 c_longlong(123)
You can see the pointers are the same and the callback return value and function return value are correct.
It is likely your NASM code isn't implementing the calling convention correctly or corrupting the stack accessing the arrays. I just did the minimum to make your Python code work. I did think it odd that length_array_out is always a length 1 double array with a value 8 times the length of the input array X. How does the NASM code know how long the arrays are?
You could be more type-correct and declare the following instead of casting the callback to a long long *:
CALLBACK = ctypes.WINFUNCTYPE(ctypes.c_double, ctypes.c_double)
CallName.argtypes = [ctypes.POINTER(ctypes.c_double),ctypes.POINTER(ctypes.c_double),CALLBACK]
CallName.restype = ctypes.POINTER(ctypes.c_int64)
#CALLBACK
def LibraryCall(ax):
bx = math.ceil(ax)
return (bx)
ret_ptr = CallName(CA_X,length_array_out,LibraryCall)
#Mark Tolonen, thank you very much for your detailed analysis. I'm posting this as an answer because the formatting of the code won't come out correctly in a comment -- but I chose your answer as the best answer.
I suspected that stack alignment may be the problem, and you eliminated ctypes as the source, so I focused on the stack. Here's what I did to make it work.
In the NASM code, I push rbp and rdi on entry, then restore them on exit. Here, before the call, I set the stack state back by popping rbp and rdi from the stack. Then I subtract 32 bytes (not 40) from rsp. When the call is finished, I restore the stack state:
pop rbp
pop rdi
sub rsp,32
call [CB_Pointer] ; The call to the callback function
add rsp,32
push rdi
push rbp
For an external function call (like to a C library function), I have to subtract 40 bytes, but for this callback I need only 32 bytes. Before your answer I had tried that with 40 bytes and it didn't work. I guess the reason is because it's not calling an external library, it's a callback to the ctypes code that's callling the dll in the first place.
One other thing. The call sends a floating-point value (xmm0) and returns an integer value, but the integer value is returned in the xmm0 register, not rax. Setting the prototype in ctypes to an integer return doesn't do it. It has to stay like this:
LibraryCB = ctypes.WINFUNCTYPE(ctypes.c_double, ctypes.c_double)
Thanks again for your reply. You showed me where to look.
P.S. length_array_out passes the length of the input array to NASM. If I pass more than one array, length_array_out will be longer with one qword for each length; currently I convert the qword to integer on entry.
I'm writing a Python module that wraps some C++ code. I used to use this function to pass a pointer from C++ to Python (the pointer would be freed by a different function
DLLEXPORT void* run(double input1, double input2)
I've added error return codes so my new function looks like
DLLEXPORT int run(double input1, double input2, void* output)
But now I can't seem to get the value of the pointer, in Python I set up the function with ctypes as follows
from ctypes import *
mydll = cdll.mydll
mydll.run.argtypes = [c_double, # input 1
c_double, # input 2
c_void_p] # output pointer
mydll.run.restype = c_int
Then I use the function by creating a new void pointer in Python and passing it to the dll function
p = c_void_p()
retval = mydll.run(1.2, 3.4, p)
print p
After running this code I am left with p equal to c_void_p(None).
Passing this pointer to other functions causes illegal access exceptions at address 0x0, so I don't think it is being updated.
I am expected some address to populate p after execution. I am missing something with ctypes? I can create a double array by allocating (c_double * 10)() and pass that to c function to be written to, why can't I pass a void pointer for the same purpose?
As eryksun's comment points out, for void* to be an output parameter, it should be void**:
# DLLEXPORT int run(double input1, double input2, void** output)
from ctypes import *
mydll = cdll.mydll
mydll.run.argtypes = [c_double, # input 1
c_double, # input 2
POINTER(c_void_p)] # output pointer
mydll.run.restype = c_int
p = c_void_p()
retval = mydll.run(1.2,3.4,byref(p))
print p.contents