[英]Python & Ctypes: Passing a struct to a function as a pointer to get back data
我已經看過其他答案,但似乎無法讓這個工作。 我正在嘗試調用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;
我想我必須在DLL填充數據數組時設置Address,Command和BlockLength的值。 需要此結構的函數將其作為指針
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 *
要將指向此類型結構的指針傳遞給函數,我首先嘗試將其初始化,如下所示:
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
這是我如何設置藝術類型:
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))
我沒有錯。 但是,當函數返回“0”表示成功時,函數返回-1,對於失敗則返回非零值。 檢查smb_request.Data的值會返回'測試字符串',因此不會有任何更改。 任何有關可能會發生什么的建議都將不勝感激。
謝謝
更新:
由於我已經得到了一些關於我的手柄是否正確的詢問,這就是我如何使用它。 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
這是一個有效的例子。 看起來你將錯誤的類型傳遞給函數。
#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;
}
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
定義的類型。 這適用於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
的結果類型設置為void*
。 默認情況下,ctypes假定函數返回int
。 你需要這樣說:
open_smbus.restype = ctypes.c_void_p
您收到錯誤是因為您使用的是c_void_p()
(請注意額外的括號)。 c_void_p
和c_void_p()
之間有一個重要的區別。 前者是一種類型 ,后者是一種類型的實例 。 c_void_p
表示C類型void*
,而c_void_p()
表示實際指針實例(默認值為0)。
嘗試改變
("Data", type(create_string_buffer(SMB_MAX_DATA_SIZE))
至
("Data", (c_char * SMB_MAX_DATA_SIZE)]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.