Python & Ctypes:将结构作为指针传递给函数以获取数据
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4351721/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Python & Ctypes: Passing a struct to a function as a pointer to get back data
提问by Hyman
I've looked through other answers but can't seem to get this to work. I'm trying to call a function within a DLL for communicating with SMBus devices. This function takes a pointer to a struct, which has an array as one of it's fields. so...
我已经查看了其他答案,但似乎无法让它发挥作用。我正在尝试在 DLL 中调用一个函数来与 SMBus 设备进行通信。此函数采用指向结构的指针,该结构具有一个数组作为其字段之一。所以...
In C:
在 C 中:
typedef struct _SMB_REQUEST
{
unsigned char Address;
unsigned char Command;
unsigned char BlockLength;
unsigned char Data[SMB_MAX_DATA_SIZE];
} SMB_REQUEST;
I think I have to set values for the Address, Command and BlockLength while the DLL fills the Data array. The function that requires this struct takes it as a pointer
我想我必须在 DLL 填充数据数组时设置地址、命令和块长度的值。需要此结构的函数将其作为指针
SMBUS_API int SmBusReadByte( SMBUS_HANDLE handle, SMB_REQUEST *request );
So I've set up the struct in Python like so:
所以我在 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))]
*Note: I've also tried ctypes.c_char*SMB_MAX_DATA_SIZE for the data type*
*注意:我也试过 ctypes.c_char*SMB_MAX_DATA_SIZE 作为数据类型*
To pass a pointer to a struct of this type to the function I have tried to initialise it first as follows:
要将指向这种类型的结构的指针传递给函数,我尝试首先按如下方式初始化它:
data = create_string_buffer(SMB_MAX_DATA_SIZE)
smb_request = SMB_REQUEST('\x53', \x00', 1, data)
This responds with:
这回应:
TypeError: expected string or Unicode object, c_char_Array_32 found
If I try leaving out the data array, like so:
如果我尝试省略数据数组,如下所示:
smb_request = SMB_REQUEST('\x53', \x00', 1)
No, error. However, then when I try to pass this to the function:
不,错误。但是,当我尝试将其传递给函数时:
int_response = smbus_read_byte(smbus_handle, smb_request))
I get:
我得到:
ArgumentError: argument 2: <type 'exceptions.TypeError'>: expected LP_SMB_REQUES
T instance instead of SMB_REQUEST
I've tried passing it as a pointer:
我试过将它作为指针传递:
int_response = smbus_read_byte(smbus_handle, ctypes.POINTER(smb_request))
and I get:
我得到:
----> 1
2
3
4
5
TypeError: must be a ctypes type
Here's how I've set up the art types:
这是我设置艺术类型的方法:
smbus_read_byte.argtypes = (ctypes.c_void_p, ctypes.POINTER(SMB_REQUEST))
I've tried casting but still no go. Can anyone shed some light on this for me?
我试过铸造,但还是不行。任何人都可以为我解释一下吗?
Update:
更新:
If I first initialise the struct like so:
如果我首先像这样初始化结构:
smb_request = SMB_REQUEST('\xA6', '\x00', chr(1), 'a test string')
and then bass by reference:
然后参考低音:
int_response = smbus_receive_byte(smbus_handle, ctypes.byref(smb_request))
I get no error. However, the function returns -1 when it should return '0' for success and non-zero for a fail. Checking the value of smb_request.Data gives back 'a test string' so no change there. Any suggestions as to what might be going on here would be greatly appreciated.
我没有错误。但是,该函数在成功时应返回“0”,失败时应返回非零时返回 -1。检查 smb_request.Data 的值会返回“一个测试字符串”,所以那里没有变化。任何关于这里可能发生的事情的建议将不胜感激。
Thanks
谢谢
UPDATE:
更新:
Since I've gotten a couple of enquiries about whether my handle is correct, here's how I'm using it. The header file for the DLL declares the following:
由于我收到了一些关于我的句柄是否正确的询问,以下是我如何使用它。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);
So here's how I'm doing this in python:
所以这就是我在 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)
I call this before making the call to smbus_read_byte(). I have tried to set open_smbus.restype = c_void_p()but I get an error: TypeError: restype must be a type, a callable, or None
我在调用 smbus_read_byte() 之前调用它。我尝试设置,open_smbus.restype = c_void_p()但出现错误:TypeError: restype must be a type, a callable, or None
采纳答案by Mark Tolonen
Here's a working example. It looks like you are passing the wrong type to the function.
这是一个工作示例。看起来您将错误的类型传递给函数。
Test DLL Code ("cl /W4 /LD x.c" on Windows)
测试 DLL 代码(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 code
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)
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
回答by Gabi Purcaru
Try changing
尝试改变
("Data", type(create_string_buffer(SMB_MAX_DATA_SIZE))
to
到
("Data", (c_char * SMB_MAX_DATA_SIZE)]
回答by Adam Rosenfield
You're almost there. You should use c_char * SMB_MAX_DATA_SIZEas the type for the definition of Data. This works for me on Mac OS X:
您快到了。您应该将其c_char * SMB_MAX_DATA_SIZE用作定义的类型Data。这在 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:
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
更新
You're having problems because you need to set the result type of open_smbusto void*. By default, ctypes assumes that functions return ints. You need to say this:
您遇到了问题,因为您需要将结果类型设置open_smbus为void*。默认情况下,ctypes 假定函数返回ints。你需要这样说:
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_pand c_void_p(). The former is a type, and the latter is an instanceof a type. c_void_prepresents the C type void*, whereas c_void_p()represents an actual pointer instance (with a default value of 0).
由于您正在使用c_void_p()(请注意额外的括号),因此出现错误。c_void_p和之间有一个重要的区别c_void_p()。前者是一个类型,后者是一个类型的实例。 c_void_p表示 C 类型void*,而c_void_p()表示实际的指针实例(默认值为 0)。

