简体   繁体   English

Python和Ctypes:将结构作为指针传递给函数以获取数据

[英]Python & Ctypes: Passing a struct to a function as a pointer to get back data

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. 我正在尝试调用DLL中的函数来与SMBus设备进行通信。 This function takes a pointer to a struct, which has an array as one of it's fields. 该函数接受一个结构的指针,该结构有一个数组作为其中一个字段。 so... 所以...

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. 我想我必须在DLL填充数据数组时设置Address,Command和BlockLength的值。 The function that requires this struct takes it as a pointer 需要此结构的函数将其作为指针

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. 但是,当函数返回“0”表示成功时,函数返回-1,对于失败则返回非零值。 Checking the value of smb_request.Data gives back 'a test string' so no change there. 检查smb_request.Data的值会返回'测试字符串',因此不会有任何更改。 Any suggestions as to what might be going on here would be greatly appreciated. 任何有关可能会发生什么的建议都将不胜感激。

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(). 我在调用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 我试图设置open_smbus.restype = c_void_p()但是我收到一个错误: TypeError: restype must be a type, a callable, or None

Here's a working example. 这是一个有效的例子。 It looks like you are passing the wrong type to the function. 看起来你将错误的类型传递给函数。

Test DLL Code ("cl /W4 /LD xc" 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

You're almost there. 你快到了。 You should use c_char * SMB_MAX_DATA_SIZE as the type for the definition of Data . 您应该使用c_char * SMB_MAX_DATA_SIZE作为Data定义的类型。 This works for me on Mac OS X: 这适用于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 UPDATE

You're having problems because you need to set the result type of open_smbus to void* . 您遇到问题是因为您需要将open_smbus的结果类型设置为void* By default, ctypes assumes that functions return int s. 默认情况下,ctypes假定函数返回int 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). 您收到错误是因为您使用的是c_void_p() (请注意额外的括号)。 There's an important distinction between c_void_p and c_void_p() . c_void_pc_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). c_void_p表示C类型void* ,而c_void_p()表示实际指针实例(默认值为0)。

Try changing 尝试改变

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

to

("Data", (c_char * SMB_MAX_DATA_SIZE)]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM