How To Pass Pointer Back In Ctypes?
Solution 1:
Normally each function you use in ctypes
should have its arguments and return type declared so Python can check for the correct number and type of arguments and convert Python object arguments to the correct C data objects. Unfortunately in this case, the normal return value for func
would be c_char_p
, but ctypes
tries to be helpful and convert a c_char_p
return value to a Python string, losing access to the raw C pointer value. Instead, you can declare the return type as POINTER(c_char)
and use cast
to retrieve the string value, which leaves the return value a LP_c_char
object that can be freed.
Here's an example. Note that declaring the correct .restype
is especially important for 64-bit Python, since the default return type is c_int
(32-bit) and a 64-bit pointer could be truncated. This code is tested with both 32- and 64-bit builds.
test.c
#include<string.h>#include<stdlib.h>#ifdef _WIN32# define API __declspec(dllexport)#else# define API#endif
API char* func(constchar* str1, constchar* str2) {
size_t len = strlen(str1) + strlen(str2) + 1;
char* tmp = malloc(len);
strcpy_s(tmp, len, str1);
strcat_s(tmp, len, str2);
return tmp;
}
API voidfreeMem(void *mem) {
free(mem);
}
test.py
import ctypes as ct
dll = ct.CDLL('./test')
dll.func.argtypes = ct.c_char_p,ct.c_char_p
dll.func.restype = ct.POINTER(ct.c_char)
dll.freeMem.argtypes = ct.c_void_p,
dll.freeMem.restype = None# Helper function to extract the return value as a Python object# and always free the pointer.deffreeMem(a,b):
p = dll.func(b'abcdef', b'ghijkl')
print(p)
s = ct.cast(p, ct.c_char_p).value
dll.freeMem(p)
return s
print(freeMem(b'abcdef', b'ghijkl'))
Output:
<ctypes.LP_c_char object at 0x00000279D7959DC0>
b'abcdefghijkl'
Solution 2:
You're on the right track.
// TestDLL.cpp#include<string.h>// strcpyextern"C" __declspec(dllexport) char* stringdup(constchar* str){
char* p = newchar[strlen(str)+1];
strcpy(p,str);
return p;
}
// if you have no good reason to use void*, use the type// you've allocated. while it usually works for built-in// types, it wouldn't work for classes (it wouldn't call// the destructor)extern"C" __declspec(dllexport) voidstringfree(char* ptr){
// you don't need to check for 0 before you delete it,// but if you allocate with new[], free with delete[] !delete [] ptr;
}
And in python:
# Test.pyimport ctypes
lib = ctypes.cdll.TestDLL
# this creates a c-style char pointer, initialized with a string whose# memory is managed by PYTHON! do not attempt to free it through the DLL!
cstr = ctypes.c_char_p("hello ctypes")
# call the dll function that returns a char pointer # whose memory is managed by the DLL.
p = lib.stringdup(cstr)
# p is just an integer containing the memory address of the # char array. therefore, this just prints the address:print p
# this prints the actual stringprint ctypes.c_char_p(p).value
# free the memory through the DLL
lib.stringfree(p)
Solution 3:
For me
#cdll.execute.restype = ctypes.c_char_p # failed to free
cdll.execute.restype = ctypes.POINTER(ctypes.c_char) # worked
p = cdll.execute('...')
print(
ctypes.cast(p, ctypes.c_char_p).value.decode('utf-8')
)
cdll.free_pointer(p)
Post a Comment for "How To Pass Pointer Back In Ctypes?"