Using Python Ctypes to pass struct pointer to a DLL function
Question: I am attempting to access a function in a DLL file using Python ctypes. The
provided function description is below.

    
    
    Prototype: Picam_ConnectDemoCamera( PicamModel model,
                                        const pichar* serial_number,
                                        PicamCameraID* id )
    
    Description: Virtually connects the software-simulated 'model' with 'serial_number'
    and returns the camera id in `_id_`
    Notes: `_id_` is optional and can be null
    

The function references a few variable types defined in the DLL. These
variables are described next

    
    
    PicamModel
    Type: enum
    Description: The camera model.
    
    PicamCameraID
    Type: Struct
    - PicamModel model: _model_ is the camera model
    - PicamComputerInterface computer_interface: computer_interface is the method of
    communication
    - pichar sensor_name [PicamStringSize_SensorName]: sensor_name contains the name of
    the sensor in the camera
    - pichar serial_number [PicamStringSize_SerialNumber]: serial_number contains the
    unique serial number of the camera
    

Note that'pichar' is defined in one of the provided header files as follows:

    
    
    typedef          char   pichar; /* character native to platform   
    

This seems straightforward, but isn't working out for me for some reason.

My understanding is as follows: I pass the function three variables:

1) a `model`, which is really an `enum`. I'm told that python ctypes doesn't
inherently support enums, and so I am passing it an integer, 2, which maps to
a particular model type.

2) a pointer to a serial number (I can make this up: an arbitrary string)

3) A pointer to `PicamCameraID`, a variable type defined within the DLL, based
on the `struct` type

After reading through SO I found a few good starting points, but am still
running out of luck. Here's my best shot so far

    
    
    def pointer(x):
          PointerToType = ctypes.POINTER(type(x))
          ptr = ctypes.cast(ctypes.addressof(x), PointerToType)
          return ptr
    
    class PicamCameraID(ctypes.Structure):
          pass
    PicamCameraID._fields_=[("model",ctypes.c_int),
                        ("computer_interface",ctypes.c_int),
                        ("sensor_name",ctypes.c_char_p),
                        ("serial_number",ctypes.c_char_p)]
    
    myid = PicamCameraID()
    model = ctypes.c_int(2)
    serialnum = ctypes.c_char_p('asdf')
    print picam.Picam_ConnectDemoCamera(model, pointer(serialnum), ctypes.byref(myid))
    

As you can hopefully see, I'm trying to create the variable type that
corresponds to the `PicamCameraID` struct defined in the DLL. My intuition was
to use the `ctypes.pointer` command to reference this when passing the
argument to the function, but I found indications to use `ctypes.byref`
instead [elsewhere](http://stackoverflow.com/questions/10484995/python-ctypes-
passing-in-pointer-and-getting-struct-back).

The code runs without error (returns 0), however, I get varying results when I
try to access the properties of the struct `PicamCameraID`:

`myid.model` returns "2" which is great that's what I specified
`myid.computer_interface` returns "1" which is fine

**BUT** the problem is `myid.serial_number` returns

    
    
    Traceback (most recent call last):   File "<pyshell#28>", line 1, in
    <module>
       myid.serial_number ValueError: invalid string pointer 0x2820303031207820
    

and `myid.sensor_name` returns:

    
    
    Traceback (most recent call last):   File "<pyshell#29>", line 1, in
    <module>
        myid.sensor_name ValueError: invalid string pointer 0x3034333120563245
    

For some reason these strings aren't being properly populated?

Any ideas would be much appreciated, I am a novice to python ctypes (and c)

Kind regards

Addition June 1 2013

    
    
    typedef enum PicamStringSize {
        PicamStringSize_SensorName     =  64,
        PicamStringSize_SerialNumber   =  64,
        PicamStringSize_FirmwareName   =  64,
        PicamStringSize_FirmwareDetail = 256 } PicamStringSize; 
    
    typedef struct PicamCameraID {
        PicamModel             model;
        PicamComputerInterface computer_interface;
        pichar                 sensor_name[PicamStringSize_SensorName];
        pichar                 serial_number[PicamStringSize_SerialNumber];
    } PicamCameraID;
    


Answer: Your definition of PicamCameraID structure is incorrect: `sensor_name` and
`serial_number` are
[arrays](http://docs.python.org/2/library/ctypes.html#ctypes-arrays):

    
    
    """
    struct PicamCameraID {
      PicamModel             model;
      PicamComputerInterface computer_interface;
      pichar                 sensor_name[PicamStringSize_SensorName];
      pichar                 serial_number[PicamStringSize_SerialNumber]; 
    };
    """
    import ctypes as c
    
    PicamStringSize_SensorName = PicamStringSize_SerialNumber = 64
    PicamModel = PicamComputerInterface = c.c_int
    pichar = c.c_char
    
    class PicamCameraID(c.Structure):
        _fields_ = [("model", PicamModel),
                    ("computer_interface", PicamComputerInterface),
                    ("sensor_name", pichar * PicamStringSize_SensorName),
                    ("serial_number", pichar * PicamStringSize_SerialNumber)]
    

It seems the second argument is just a string, so you don't need to apply
`pointer()` to it. Here's the `ctypes` prototype for
`Picam_ConnectDemoCamera()` function:

    
    
    """
    Picam_ConnectDemoCamera(PicamModel model, const pichar* serial_number,
                            PicamCameraID* id)
    """
    pichar_p = c.c_char_p # assume '\0'-terminated C strings
    Picam_ConnectDemoCamera.argtypes = [PicamModel, pichar_p,
                                        c.POINTER(PicamCameraID)]
    Picam_ConnectDemoCamera.restype = c.c_int # assume it returns C int
    

To call it:

    
    
    picam_id = PicamCameraID()
    rc = Picam_ConnectDemoCamera(2, "serial number here", c.byref(picam_id))
    print(rc)
    print(picam_id.sensor_name.value) # C string
    print(picam_id.sensor_name.raw)   # raw memory
    

