I am accessing an API and can't get the data returned. The two float pointers will point to an array of data. I must assume the API is working properly. A different function call provides a the length of the data I am retrieving. This values is length down below when attempted.
C Header for Function
int function(int, float * data1, float * data2)
ctypes setup
dll.function.argtypes = (c_int, POINTER(c_float), POINTER(c_float))
dll.function.restypes = c_int
Failed Attempt 1:
x = c_float()
y = c_float()
status = dll.function(1, byref(x), byref(y))
Program crashes OR Access violation writing.
Failed Attempt 2:
x = POINTER(c_float)()
y = POINTER(c_float)()
status = dll.function(1, x, y)
Null Pointer Error
Failed Attempt 3:
dll.function.argtypes = (c_int, c_void_p, c_void_p)
x = c_void_p()
y = c_void_p()
status = dll.function(1, x, y)
Null Pointer Error
Failed Attempt 4:
array = c_float * length
x = array()
y = array()
status = dll.function(1, byref(x), byref(y))
Program crashes
Failed Attempt 5:
array = c_float * length
x = POINTER(array)()
y = POINTER(array)()
status = dll.function(1, x, y)
Null Pointer Error OR ArgumentError: expected LP_c_float instance instead of LP_c_float_Array_[length]
Failed Attempt 6:
x = (c_float*length)()
y = (c_float*length)()
a = cast(x, POINTER(c_float))
b = cast(y, POINTER(c_float))
status = dll.function(1, a, b)
Program crashes
What am I missing and why?
I believe the argtypes are correct. I am attempting to meet them properly, but there continues to be an issues. Do I need to "malloc" the memory somehow? (I'm sure I need to free after I get the data).
This is on Windows 7 with Python 2.7 32-bit.
I have looked through other similar issues and am not finding a solution. I am wondering if, at this point, I can blame the API for this issue.
解决方案
Dealing with pointers and arrays is explained in [Python 3]: Type conversions.
I prepared a dummy example for you.
main.c:
#if defined(_WIN32)
# define DECLSPEC_DLLEXPORT __declspec(dllexport)
#else
# define DECLSPEC_DLLEXPORT
#endif
static int kSize = 5;
DECLSPEC_DLLEXPORT int size() {
return kSize;
}
DECLSPEC_DLLEXPORT int function(int dummy, float *data1, float *data2) {
for (int i = 0; i < kSize; i++) {
data1[i] = dummy * i;
data2[i] = -dummy * (i + 1);
}
return 0;
}
code.py:
#!/usr/bin/env python
import sys
import ctypes
c_float_p = ctypes.POINTER(ctypes.c_float)
def main():
dll_dll = ctypes.CDLL("./dll.so")
size_func = dll_dll.size
size_func.argtypes = []
size_func.restype = ctypes.c_int
function_func = dll_dll.function
function_func.argtypes = [ctypes.c_int, c_float_p, c_float_p]
function_func.restype = ctypes.c_int
size = size_func()
print(size)
data1 = (ctypes.c_float * size)()
data2 = (ctypes.c_float * size)()
res = function_func(1, ctypes.cast(data1, c_float_p), ctypes.cast(data2, c_float_p))
for i in range(size):
print(data1[i], data2[i])
if __name__ == "__main__":
print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
main()
Notes:
The C part tries to mimic what your .dll does (or at least what I understood):
size - gets the arrays sizes
function - populates the arrays (till their size - assuming that they were properly allocated by the caller)
Python part is straightforward:
Load the .dll
Define argtypes and restype (in your code it's restype's) for the 2 functions (for size_func not necessary)
Get the lengths
Initialize the arrays
Pass them to function_func using ctypes.cast
Output (on Lnx, as building the C code is much simpler, but works on Win as well):
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050043861]> gcc -shared -o dll.so main.c
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050043861]> python3 code.py
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] on linux
5
0.0 -1.0
1.0 -2.0
2.0 -3.0
3.0 -4.0
4.0 -5.0