16

I am trying this simple ctypes example and getting the error mentioned

>>> from ctypes import create_string_buffer
>>> str = create_string_buffer("hello")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python32\lib\ctypes\__init__.py", line 59, in create_string_buffer
buf.value = init
TypeError: str/bytes expected instead of str instance

Does anyone know what am I doing wrong?

On the same note, I am trying to pass a pointer to a string to a C function from my python code so that I can perform some string operation there and return another string. Can someone give me some sample code on how to do that?

extern "C" __declspec(dllexport) char * echo(char* c)
{
      // do stuff
    return c;
}
1
  • 2
    Please don't ever assign to str (not that that's the problem here). Shadowing builtins is evil. Commented Aug 29, 2011 at 23:42

3 Answers 3

15

You're using Python 3, and strings (str-type) in Python 3 are Unicode objects. create_string_buffer creates C char arrays, so you are required to pass a bytes object. If you have a str object encode with an appropriate encoding to create a bytes object. Note there is a create_unicode_buffer that creates C wchar arrays as well and takes Python 3 str objects.

Python 3.2.1 (default, Jul 10 2011, 21:51:15) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes
>>> ctypes.create_unicode_buffer('abcd')
<ctypes.c_wchar_Array_5 object at 0x00C2D300>
>>> ctypes.create_string_buffer(b'abcd')
<ctypes.c_char_Array_5 object at 0x00C2D1C0>
>>> ctypes.create_string_buffer('abcd'.encode('utf-8'))
<ctypes.c_char_Array_5 object at 0x00C2D300>

In regards to the 2nd part of your question, do you want to edit the existing string or return an entirely new string allocated on the heap? If the former, use create_string_buffer to pass a mutable string to the function, then modifying it in place will work. Here's a simple example calling _strupr() from the Windows MSVCRT library:

Python 3.2.1 (default, Jul 10 2011, 21:51:15) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes
>>> c = ctypes.CDLL('msvcr90.dll')
>>> strupr = c._strupr
>>> a=ctypes.create_string_buffer(b'abc')
>>> strupr.restype=ctypes.c_char_p
>>> strupr(a)
b'ABC'

Note setting the restype (result type) of the function to a c_char_p tells ctypes to convert the return value to a bytes object. Also note that _strupr converts a string in place, so a mutable string buffer must be passed. Only pass Python byte strings to C functions that take const char*, since modifying Python strings directly via ctypes is BadTM.

Sign up to request clarification or add additional context in comments.

2 Comments

Does ctypes.create_unicode_buffer('abcd') work for you? It isn't working for me on Python 3.2 (not 3.2.1 which I see you have) on Ubuntu 11.04 64-bit, and it clearly isn't working for @Helali.
Yes, it works for me. I looked in the release notes for 3.2.1 and don't see requiring bytes for create_string_buffer as a fix, but I did find this issue (bugs.python.org/issue8306) saying str to bytes implicit conversions should not be allowed.
11

With regards to getting it working, if you pass it a bytes object, it works:

>>> import ctypes
>>> ctypes.create_string_buffer(b'hello')
<ctypes.c_char_Array_6 object at 0x25258c0>

Looking at the code for create_string_buffer:

def create_string_buffer(init, size=None):
    """create_string_buffer(aBytes) -> character array
    create_string_buffer(anInteger) -> character array
    create_string_buffer(aString, anInteger) -> character array
    """
    if isinstance(init, (str, bytes)):
        if size is None:
            size = len(init)+1
        buftype = c_char * size
        buf = buftype()
        buf.value = init
        return buf
    elif isinstance(init, int):
        buftype = c_char * init
        buf = buftype()
        return buf
    raise TypeError(init)

Doing it directly,

>>> (ctypes.c_char * 10)().value = b'123456789'

This works fine.

>>> (ctypes.c_char * 10)().value = '123456789'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: str/bytes expected instead of str instance

This shows that same behaviour. Looks to me as though you've found a bug.

Time to visit http://bugs.python.org. There are a few bugs related to c_char and create_string_buffer which are in the same field, but none reporting that giving it a str now fails (but there are definite examples showing it used to work in Py3K).

3 Comments

Thanks for the solution with passing as bytes object. Could you please answer the later part of the question? How can I print the string value returned as a pointer from that C method in python?
@Helali: I don't know. I haven't ever dealt with C code in Python. If you have a ctypes.c_char_Array_* instance it's just thingy.value, but I presume that's not your question.
@eryksun: I don't care about that; c_char arrays used to accept str, and still claims to but doesn't any more. Thus it is a regression. Whether they choose to change the documented behaviour rather than fix the regression is their choice. But it is a bug.
0

The argument is the size of the buffer required for the string, so pass it the length the buffer should be. It will create a char array of that size.

>>> from ctypes import create_string_buffer
>>> buf = create_string_buffer(20)
>>> buf
<ctypes.c_char_Array_20 object at 0x355348>
>>> 

1 Comment

Well I got that. But how do I initialize that buffer to a string value? And finally how can I pass that to the C method? Thanks.

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.