title: Python & Ctypes: Passing a struct to a function as a pointer to get back data
body: <p>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...</p>
  
  <p>In C:</p>
  
  <pre><code>typedef struct _SMB_REQUEST
  {
      unsigned char Address;
      unsigned char Command;
      unsigned char BlockLength;
      unsigned char Data[SMB_MAX_DATA_SIZE];
  } SMB_REQUEST;
  </code></pre>
  
  <p>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</p>
  
  <pre><code>SMBUS_API int SmBusReadByte( SMBUS_HANDLE handle, SMB_REQUEST *request );
  </code></pre>
  
  <p>So I've set up the struct in Python like so:</p>
  
  <pre><code>class SMB_REQUEST(ctypes.Structure):
      _fields_ = [("Address", c_char),
              ("Command", c_char),
              ("BlockLength", c_char),
              ("Data", type(create_string_buffer(SMB_MAX_DATA_SIZE))]
  </code></pre>
  
  <p>*Note: I've also tried ctypes.c_char*SMB_MAX_DATA_SIZE for the data type*</p>
  
  <p>To pass a pointer to a struct of this type to the function I have tried to initialise it first as follows:</p>
  
  <pre><code>data = create_string_buffer(SMB_MAX_DATA_SIZE)
  smb_request = SMB_REQUEST('\x53', \x00', 1, data)
  </code></pre>
  
  <p>This responds with:</p>
  
  <pre><code>TypeError: expected string or Unicode object, c_char_Array_32 found
  </code></pre>
  
  <p>If I try leaving out the data array, like so:</p>
  
  <pre><code>smb_request = SMB_REQUEST('\x53', \x00', 1)
  </code></pre>
  
  <p>No, error.
  However, then when I try to pass this to the function:</p>
  
  <pre><code>int_response =  smbus_read_byte(smbus_handle, smb_request))
  </code></pre>
  
  <p>I get:</p>
  
  <pre><code>ArgumentError: argument 2: &lt;type 'exceptions.TypeError'&gt;: expected LP_SMB_REQUES
  T instance instead of SMB_REQUEST
  </code></pre>
  
  <p>I've tried passing it as a pointer:</p>
  
  <pre><code>int_response =  smbus_read_byte(smbus_handle, ctypes.POINTER(smb_request))
  </code></pre>
  
  <p>and I get:</p>
  
  <pre><code>----&gt; 1
        2
        3
        4
        5
  
  TypeError: must be a ctypes type
  </code></pre>
  
  <p>Here's how I've set up the art types:</p>
  
  <pre><code>smbus_read_byte.argtypes = (ctypes.c_void_p, ctypes.POINTER(SMB_REQUEST))
  </code></pre>
  
  <p>I've tried casting but still no go. Can anyone shed some light on this for me?</p>
  
  <p><strong>Update:</strong></p>
  
  <p>If I first initialise the struct like so:</p>
  
  <pre><code>smb_request = SMB_REQUEST('\xA6', '\x00', chr(1), 'a test string')
  </code></pre>
  
  <p>and then bass by reference:</p>
  
  <pre><code>int_response =  smbus_receive_byte(smbus_handle, ctypes.byref(smb_request))
  </code></pre>
  
  <p>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.</p>
  
  <p>Thanks</p>
  
  <p><strong>UPDATE:</strong></p>
  
  <p>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:</p>
  
  <pre><code>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);
  </code></pre>
  
  <p>So here's how I'm doing this in python:</p>
  
  <pre><code>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)
  </code></pre>
  
  <p>I call this before making the call to smbus_read_byte(). I have tried to set <code>open_smbus.restype = c_void_p()</code> but I get an error: <code>TypeError: restype must be a type, a callable, or None</code></p>