Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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...

In 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

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

So I've set up the struct in Python like so:

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*

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.

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:

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:

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

like image 203
Jack Avatar asked Dec 04 '10 03:12

Jack


People also ask

What is Python used for?

Python is a computer programming language often used to build websites and software, automate tasks, and conduct data analysis. Python is a general-purpose language, meaning it can be used to create a variety of different programs and isn't specialized for any specific problems.

Is Python easy to learn?

Python is widely considered among the easiest programming languages for beginners to learn. If you're interested in learning a programming language, Python is a good place to start. It's also one of the most widely used.

What is the basic language of Python?

Python is written in C (actually the default implementation is called CPython).

Is Python coding good?

Python is undoubtedly considered a top programming language at the same level as JavaScript or C++, and it's one of the most used languages by businesses and enterprises. Even though it's almost 30 years old, Python is still relevant, given its ease of use, its vibrant community, and many applications.


1 Answers

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)

#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

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 
like image 169
Mark Tolonen Avatar answered Sep 28 '22 07:09

Mark Tolonen