Python & Ctypes: pasar una estructura a una función como puntero para recuperar datos

He revisado otras respuestas pero parece que no puedo hacer que esto funcione. Estoy intentando llamar a una función dentro de una DLL para comunicarme con dispositivos SMBus. Esta función lleva un puntero a una estructura, que tiene una matriz como uno de sus campos. asi que…

Cía:

typedef struct _SMB_REQUEST { unsigned char Address; unsigned char Command; unsigned char BlockLength; unsigned char Data[SMB_MAX_DATA_SIZE]; } SMB_REQUEST; 

Creo que tengo que establecer valores para la Dirección, Comando y Longitud del Bloque mientras la DLL llena la matriz de Datos. La función que requiere esta estructura lo toma como un puntero.

 SMBUS_API int SmBusReadByte( SMBUS_HANDLE handle, SMB_REQUEST *request ); 

Así que he configurado la estructura en Python así:

 class SMB_REQUEST(ctypes.Structure): _fields_ = [("Address", c_char), ("Command", c_char), ("BlockLength", c_char), ("Data", type(create_string_buffer(SMB_MAX_DATA_SIZE))] 

* Nota: También he probado ctypes.c_char * SMB_MAX_DATA_SIZE para el tipo de datos *

Para pasar un puntero a una estructura de este tipo a la función, he intentado inicializarlo primero de la siguiente manera:

 data = create_string_buffer(SMB_MAX_DATA_SIZE) smb_request = SMB_REQUEST('\x53', \x00', 1, data) 

Esto responde con:

 TypeError: expected string or Unicode object, c_char_Array_32 found 

Si bash omitir la matriz de datos, así:

 smb_request = SMB_REQUEST('\x53', \x00', 1) 

No hay error. Sin embargo, entonces cuando bash pasar esto a la función:

 int_response = smbus_read_byte(smbus_handle, smb_request)) 

Yo obtengo:

 ArgumentError: argument 2: : expected LP_SMB_REQUES T instance instead of SMB_REQUEST 

He intentado pasarlo como un puntero:

 int_response = smbus_read_byte(smbus_handle, ctypes.POINTER(smb_request)) 

y me sale:

 ----> 1 2 3 4 5 TypeError: must be a ctypes type 

Así es como he configurado los tipos de arte:

 smbus_read_byte.argtypes = (ctypes.c_void_p, ctypes.POINTER(SMB_REQUEST)) 

He intentado el casting pero todavía no voy. ¿Alguien puede arrojar algo de luz sobre esto para mí?

Actualizar:

Si primero inicializo la estructura de esta manera:

 smb_request = SMB_REQUEST('\xA6', '\x00', chr(1), 'a test string') 

y luego el bajo por referencia:

 int_response = smbus_receive_byte(smbus_handle, ctypes.byref(smb_request)) 

No me sale ningún error. Sin embargo, la función devuelve -1 cuando debería devolver ‘0’ para el éxito y no cero para un error. La comprobación del valor de smb_request.Data devuelve ‘una cadena de prueba’, por lo que no hay cambios allí. Cualquier sugerencia sobre lo que podría estar pasando aquí sería muy apreciada.

Gracias

ACTUALIZAR:

Desde que recibí un par de preguntas sobre si mi identificador es correcto, así es como lo estoy usando. El archivo de encabezado de la DLL declara lo siguiente:

 typedef void *SMBUS_HANDLE; // // This function call initializes the SMBus, opens the driver and // allocates the resources associated with the SMBus. // All SMBus API calls are valid // after making this call except to re-open the SMBus. // SMBUS_API SMBUS_HANDLE OpenSmbus(void); 

Así que aquí es cómo estoy haciendo esto en Python:

 smbus_handle = c_void_p() # NOTE: I have also tried it without this line but same result open_smbus = CDLL('smbus.dll').OpenSmbus smbus_handle = open_smbus() print 'SMBUS_API SMBUS_HANDLE OpenSmbus(void): ' + str(smbus_handle) 

Llamo a esto antes de hacer la llamada a smbus_read_byte (). He intentado establecer open_smbus.restype = c_void_p() pero open_smbus.restype = c_void_p() un error: TypeError: restype must be a type, a callable, or None

Aquí hay un ejemplo de trabajo. Parece que está pasando el tipo incorrecto a la función.

Código DLL de prueba (“cl / W4 / LD xc” en Windows)

 #include  #define SMBUS_API __declspec(dllexport) #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) { unsigned char i; for(i = 0; i < request->BlockLength; i++) request->Data[i] = i; return request->BlockLength; } SMBUS_API SMBUS_HANDLE OpenSmbus(void) { return (void*)0x12345678; } 

Codigo piton

 from ctypes import * SMB_MAX_DATA_SIZE = 5 ARRAY5 = c_ubyte * SMB_MAX_DATA_SIZE class SMB_REQUEST(Structure): _fields_ = [ ("Address", c_ubyte), ("Command", c_ubyte), ("BlockLength", c_ubyte), ("Data", ARRAY5)] smbus_read_byte = CDLL('x').SmBusReadByte smbus_read_byte.argtypes = [c_void_p,POINTER(SMB_REQUEST)] smbus_read_byte.restype = c_int open_smbus = CDLL('x').OpenSmbus open_smbus.argtypes = [] open_smbus.restype = c_void_p handle = open_smbus() print 'handle = %08Xh' % handle smb_request = SMB_REQUEST(1,2,5) print 'returned =',smbus_read_byte(handle,byref(smb_request)) print 'Address =',smb_request.Address print 'Command =',smb_request.Command print 'BlockLength =',smb_request.BlockLength for i,b in enumerate(smb_request.Data): print 'Data[%d] = %02Xh' % (i,b) 

Salida

 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 

Ya casi estás ahí. Debería usar c_char * SMB_MAX_DATA_SIZE como el tipo para la definición de Data . Esto me funciona en Mac OS X:

Biblioteca compartida:

 $ cat test.c #include  #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 

Controlador de Python:

 $ 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 

ACTUALIZAR

Está teniendo problemas porque necesita establecer el tipo de resultado de open_smbus en void* . Por defecto, ctypes asume que las funciones devuelven int s. Necesitas decir esto:

 open_smbus.restype = ctypes.c_void_p 

c_void_p() un error porque estaba usando c_void_p() (note los paréntesis adicionales). Hay una distinción importante entre c_void_p y c_void_p() . El primero es un tipo , y el último es una instancia de un tipo. c_void_p representa el tipo c de void* , mientras que c_void_p() representa una instancia de puntero real (con un valor predeterminado de 0).

Intenta cambiar

 ("Data", type(create_string_buffer(SMB_MAX_DATA_SIZE)) 

a

 ("Data", (c_char * SMB_MAX_DATA_SIZE)]