How to build a python string from a ctype structure?


I'm using ctypes and I've defined this struct in order to pass parameters

class my_struct(ctypes.Structure):
    _fields_ = [ ("buffer", ctypes.c_char * BUFSIZE),
                 ("size", ctypes.c_int )]

Then I call the C function using the following code, but I don't know how to create a string from the struct I've created.

class Client():

    def __init__(self):
        self.__proto = my_struct()
        self.client = ctypes.cdll.LoadLibrary(r"I:\bin\client.dll")

    def version(self):
        ret = self.client.execute(ctypes.byref(self.__proto))
        my_string = self.__proto.buffer[:self.__proto.size]

I want to create a python string using the first n bytes of the buffer (the buffer contains NULL characters but I have to handle this situation and create the string with /0x00 characters if necesary). The asignation

my_string = self.__proto.buffer[:self.__proto.size]

is not working bacause truncates the string if 0x00 appears. Any idea is welcome. Thanks in advance.

Your problem is that ctypes tries to do some magic for you with char arrays, auto-converting them into NUL-terminated strings. You can get around this magic by using the ctypes.c_byte type instead of ctypes.c_char and retrieving the value as a string with ctypes.string_at. You can make accessing the member a little nicer with a helper property on the structure class, such as:

import ctypes
BUFSIZE = 1024

class my_struct(ctypes.Structure):
    _fields_ = [ ("_buffer", ctypes.c_byte * BUFSIZE),
                 ("size", ctypes.c_int )]

    def buffer():
        def fget(self):
            return ctypes.string_at(self._buffer, self.size)
        def fset(self, value):
            size = len(value)
            if size > BUFSIZE:
                raise ValueError("value %s too large for buffer",
            self.size = size
            ctypes.memmove(self._buffer, value, size)
        return property(fget, fset)
    buffer = buffer()

proto = my_struct()
proto.buffer = "here\0are\0some\0NULs"
print proto.buffer.replace("\0", " ")