Python&Ctypes:将一个结构传递给一个函数,作为取回数据的指针

我已经通过其他的答案看,但似乎无法得到这个工作。 我正在试图调用DLL中的函数来与SMBus设备进行通信。 这个函数需要一个指向结构的指针,该结构有一个数组作为它的一个字段。 所以…

在C:

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

我想我必须设置值,地址,命令和BlockLength,而DLL填充数据数组。 需要此结构的函数将其作为指针

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

所以我在Python中设置了这样的结构:

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

*注意:我也尝试过ctypes.c_char * SMB_MAX_DATA_SIZE作为数据types*

要传递一个指向这个types的结构体的指针,我试图初始化它,如下所示:

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

这回应了:

 TypeError: expected string or Unicode object, c_char_Array_32 found 

如果我尝试删除数据数组,如下所示:

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

不,错误 然而,当我尝试通过这个函数:

 int_response = smbus_read_byte(smbus_handle, smb_request)) 

我得到:

 ArgumentError: argument 2: <type 'exceptions.TypeError'>: expected LP_SMB_REQUES T instance instead of SMB_REQUEST 

我试过把它作为一个指针传递:

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

我得到:

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

以下是我设置艺术types的方法:

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

我已经尝试了铸造,但仍然没有去。 任何人都可以为我阐明这一点吗?

更新:

如果我首先初始化这样的结构:

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

然后通过参考低音:

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

我没有错误。 但是,函数返回-1时,应该返回“0”成功,失败返回非零。 检查smb_request.Data的值会返回“testingstring”,所以在那里没有改变。 任何build议,可能会发生在这里将不胜感激。

谢谢

更新:

由于我已经收到了一些关于我的句柄是否正确的问题,下面是我如何使用它。 该DLL的头文件声明以下内容:

 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); 

所以这里是我如何在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) 

在对smbus_read_byte()进行调用之前,我先调用它。 我试图设置open_smbus.restype = c_void_p()但我得到一个错误: TypeError: restype must be a type, a callable, or None

这是一个工作的例子。 它看起来像你正在传递错误的types的function。

testingDLL代码(Windows上的“cl / W4 / LD xc”)

 #include <stdio.h> #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; } 

Python代码

 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) 

产量

 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 

你快到了 您应该使用c_char * SMB_MAX_DATA_SIZE作为Data定义的types。 这适用于我在Mac OS X上:

共享库:

 $ 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驱动程序:

 $ 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

你有问题,因为你需要设置open_smbus的结果types为void* 。 默认情况下,ctypes假定函数返回int s。 你需要说这个:

 open_smbus.restype = ctypes.c_void_p 

你正在使用c_void_p() (注意多余的圆括号)。 c_void_pc_void_p()之间有一个重要的区别。 前者是一种types ,后者是一种types的实例c_void_p表示Ctypesvoid* ,而c_void_p()表示一个实际的指针实例(默认值为0)。

尝试改变

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

 ("Data", (c_char * SMB_MAX_DATA_SIZE)]