5

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.

1
  • In Python 2.6.3, I've tested creating a ctypes array like your buffer, but I can't see that it gets truncated actually. Like this: ar = (ctypes.c_char*10)(); ar.value = "test"; assert ar[:6] == 'test\x00\x00'. Am I missing something? Commented Feb 26, 2011 at 23:52

2 Answers 2

3

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",
                                 repr(value))
            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", " ")
Sign up to request clarification or add additional context in comments.

Comments

0

I think you need to send a pointer to a C string in your my_struct, and not a C string directly, because C strings are null-terminated. Try to do it like this:

import ctypes

BUFSIZE = 10

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

cstr = (ctypes.c_char * BUFSIZE)()
proto = my_struct(cstr)

1 Comment

I see what you're trying to do, but that's not correct if the structure expected by the library the OP is interfacing with has the buffer directly embedded in the struct.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.