Change
foo = POINTER(temp_foo)
to
foo = pointer(temp_foo)
can solve the problem.
Please see http://docs.python.org/library/ctypes.html#ctypes-pointers for more information.
Answer from czchen on Stack OverflowChange
foo = POINTER(temp_foo)
to
foo = pointer(temp_foo)
can solve the problem.
Please see http://docs.python.org/library/ctypes.html#ctypes-pointers for more information.
Your bar function has an incorrect definition, I guess you mean it is struct FOO_ *bar(int);?
The Python code is wrong in the sense that foo_parameter is never declared, so I'm not 100% sure what you want to do. I assume you want to pass a parameter of your python-declared foo, which is an instance of a struct FOO_, into the C bar(int) and get back a pointer to struct FOO_.
You don't need POINTER to do that, the following will work:
#!/usr/bin/env python
from ctypes import *
class foo(Structure):
_fields_=[("i",c_int),
("b1",POINTER(c_int)),
("w1",POINTER(c_float))]
myclib = cdll.LoadLibrary("./libexample.so")
temp_foo = foo(1,None,None)
foovar = myclib.bar(temp_foo.i)
myclib.foo_write(foovar)
Since CTypes will wrap the return type of bar() in a pointer-to-struct for you.
Here's a working example. It looks like you are passing the wrong type to the function and the structure was declared incorrectly for the Data member. Note below how to declare the type of a fixed-size array when declaring the Python structure.
test.c ("cl /W4 /LD .c" on Windows):
#include <stdio.h>
#ifdef _WIN32
# define SMBUS_API __declspec(dllexport)
#else
# define SMBUS_API
#endif
#define SMB_MAX_DATA_SIZE 5
typedef void* SMBUS_HANDLE;
typedef struct _SMB_REQUEST {
unsigned char Address;
unsigned char Command;
unsigned char BlockLength;
unsigned char Data[SMB_MAX_DATA_SIZE];
} SMB_REQUEST;
SMBUS_API int SmBusReadByte(SMBUS_HANDLE handle, SMB_REQUEST *request) {
for(unsigned char i = 0; i < request->BlockLength; ++i)
request->Data[i] = i;
return request->BlockLength;
}
SMBUS_API SMBUS_HANDLE OpenSmbus(void) {
return (void*)0x12345678;
}
test.py:
import ctypes as ct
SMB_MAX_DATA_SIZE = 5
ARRAY = ct.c_ubyte * SMB_MAX_DATA_SIZE
class SMB_REQUEST(ct.Structure):
_fields_ = (('Address', ct.c_ubyte),
('Command', ct.c_ubyte),
('BlockLength', ct.c_ubyte),
('Data', ARRAY))
dll = ct.CDLL('./test')
smbus_read_byte = dll.SmBusReadByte
smbus_read_byte.argtypes = ct.c_void_p, ct.POINTER(SMB_REQUEST)
smbus_read_byte.restype = ct.c_int
open_smbus = dll.OpenSmbus
open_smbus.argtypes = ()
open_smbus.restype = ct.c_void_p
handle = open_smbus()
print(f'{handle = :08X}h')
smb_request = SMB_REQUEST(1, 2, 5)
print(f'returned = {smbus_read_byte(handle, smb_request)}')
print(f'Address = {smb_request.Address}')
print(f'Command = {smb_request.Command}')
print(f'BlockLength = {smb_request.BlockLength}')
for i, b in enumerate(smb_request.Data):
print(f'Data[{i}] = {b:02X}h')
Output:
handle = 12345678h
returned = 5
Address = 1
Command = 2
BlockLength = 5
Data[0] = 00h
Data[1] = 01h
Data[2] = 02h
Data[3] = 03h
Data[4] = 04h
You're almost there. You should use c_char * SMB_MAX_DATA_SIZE as the type for the definition of Data. This works for me on Mac OS X:
Shared library:
$ cat test.c
#include <stdio.h>
#define SMB_MAX_DATA_SIZE 16
typedef struct _SMB_REQUEST
{
unsigned char Address;
unsigned char Command;
unsigned char BlockLength;
unsigned char Data[SMB_MAX_DATA_SIZE];
} SMB_REQUEST;
int SmBusReadByte(void *handle, SMB_REQUEST *request)
{
printf("SmBusReadByte: handle=%p request=[%d %d %d %s]\n", handle,
request->Address, request->Command, request->BlockLength, request->Data);
return 13;
}
$ gcc test.c -fPIC -shared -o libtest.dylib
Python driver:
$ cat test.py
import ctypes
SMB_MAX_DATA_SIZE = 16
class SMB_REQUEST(ctypes.Structure):
_fields_ = [("Address", ctypes.c_ubyte),
("Command", ctypes.c_ubyte),
("BlockLength", ctypes.c_ubyte),
("Data", ctypes.c_char * SMB_MAX_DATA_SIZE)]
libtest = ctypes.cdll.LoadLibrary('libtest.dylib')
req = SMB_REQUEST(1, 2, 3, 'test')
result = libtest.SmBusReadByte(ctypes.c_voidp(0x12345678), ctypes.byref(req))
print 'result: %d' % result
$ python test.py
SmBusReadByte: handle=0x12345678 request=[1 2 3 test]
result: 13
UPDATE
You're having problems because you need to set the result type of open_smbus to void*. By default, ctypes assumes that functions return ints. You need to say this:
open_smbus.restype = ctypes.c_void_p
You were getting an error because you were using c_void_p() (note the extra parentheses). There's an important distinction between c_void_p and c_void_p(). The former is a type, and the latter is an instance of a type. c_void_p represents the C type void*, whereas c_void_p() represents an actual pointer instance (with a default value of 0).
Your definition of PicamCameraID structure is incorrect: sensor_name and serial_number are arrays:
"""
struct PicamCameraID {
PicamModel model;
PicamComputerInterface computer_interface;
pichar sensor_name[PicamStringSize_SensorName];
pichar serial_number[PicamStringSize_SerialNumber];
};
"""
import ctypes as c
PicamStringSize_SensorName = PicamStringSize_SerialNumber = 64
PicamModel = PicamComputerInterface = c.c_int
pichar = c.c_char
class PicamCameraID(c.Structure):
_fields_ = [("model", PicamModel),
("computer_interface", PicamComputerInterface),
("sensor_name", pichar * PicamStringSize_SensorName),
("serial_number", pichar * PicamStringSize_SerialNumber)]
It seems the second argument is just a string, so you don't need to apply pointer() to it. Here's the ctypes prototype for Picam_ConnectDemoCamera() function:
"""
Picam_ConnectDemoCamera(PicamModel model, const pichar* serial_number,
PicamCameraID* id)
"""
pichar_p = c.c_char_p # assume '\0'-terminated C strings
Picam_ConnectDemoCamera.argtypes = [PicamModel, pichar_p,
c.POINTER(PicamCameraID)]
Picam_ConnectDemoCamera.restype = c.c_int # assume it returns C int
To call it:
picam_id = PicamCameraID()
rc = Picam_ConnectDemoCamera(2, "serial number here", c.byref(picam_id))
print(rc)
print(picam_id.sensor_name.value) # C string
print(picam_id.sensor_name.raw) # raw memory
Likely, you're not entering the exact structure fields in the exact same order. If you don't, then you usually get random data or segfaults. To access a library with ctypes, you usually need to look inside the header (.h file) to copy the exact structure definition. Alternatively, use cffi with ffi.verify() if you don't want to depend on the precise layout.
The problem is that the higher level "POINTER" from ctypes is, in Python, a different object than "a generic pointer" (ctypes.CArgObject by ctypes.byref)which is returned or a single number representing a memory address (which is what is returned by ctype's adrresof) - you can either annotate your function to receive a `ctypes.c_voidp and call it with _lib.mymethod(ctypes.addressof(a)) instead -
Or if you want to work on the stronged-typed side to avoid errors that would crash Python (a type error raises a Python exception instead - a wrong parameter passed to a C unction would cause a segmentation fault on the Python interpreter itself), you have to create a variable to hold the new "type" which is a POINTER to your structure - and then create an instance of this type with the address of your structure:
mystruct_pointer = ctypes.POINTER(mystruct)
_lib.mymethod.argtypes = (mystruct_pointer,)
_lib.mymethod.restype = ctypes.c_int
s = mystruct()
_lib.mymethod(mystruct_pointer.from_address(ctypes.addressof(s)))
(I know that this is an old question, but I think the accepted answer is an unnecessary workaround, so I want to leave this here for posterity.)
Actually ctypes should explicitly support using byref() to pass a pointer like that:
ctypes exports the
byref()function which is used to pass parameters by reference. The same effect can be achieved with thepointer()function, althoughpointer()does a lot more work since it constructs a real pointer object, so it is faster to usebyref()if you don’t need the pointer object in Python itself.
The likely cause of this is that you have defined your struct in more than one place (e.g. in different modules) - if the argtypes assignment sees one definition and the function call sees the other, this confusing error arises. In other words, ctypes tries to match two mystruct types that are (probably) identical in contents, and have the exact same name, but they are not the same type. As long as the base struct type is a single type object, it doesn't matter if you construct a pointer to it using pointer(), byref() or POINTER()() - ctypes will detect that the underlying (pointed-to) type is the same.
To verify if this is the case, try assert(_lib.mymethod.argtypes[0]._type_ == type(s)) right before calling the external function.