0

Usually in Python when you do an assignment of a variable, you don't get a copy - you just get a second reference to the same object.

a = b'Hi'
b = a
a is b  # shows True

Now when you use ctypes.create_string_buffer to get a buffer to e.g. interact with a Windows API function, you can use the .raw attribute to access the bytes. But what if you want to access those bytes after you've deleted the buffer?

c = ctypes.create_string_buffer(b'Hi')
d = c.raw
e = c.raw
d is e  # shows False?
d == e  # shows True as you'd expect
c.raw is c.raw  # shows False!
del c

At this point are d and e still safe to use? From my experimentation it looks like the .raw attribute makes copies when you access it, but I can't find anything in the official documentation to support that.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622

1 Answers1

2

.raw returns a separate, immutable Python bytes object each time called it may be an interned version (d is e could return True) but is safe to use.

An easy test:

>>> x=ctypes.create_string_buffer(1)
>>> a = x.raw
>>> x[0] = 255
>>> b = x.raw
>>> a
b'\x00'
>>> b
b'\xff'

This point in the documentation comments on this for the ctypes type c_char_p, but it applies to other ctypes types like c_char_Array as well (emphasis mine):

>>> s = c_char_p()
>>> s.value = b"abc def ghi"
>>> s.value
b'abc def ghi'
>>> s.value is s.value
False

... Why is it printing False? ctypes instances are objects containing a memory block plus some descriptors accessing the contents of the memory. Storing a Python object in the memory block does not store the object itself, instead the contents of the object is stored. Accessing the contents again constructs a new Python object each time!

Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
  • Thanks, I never thought to check the docs of other functions but it makes sense that they would work the same way. Also makes sense that they would build a new object each time, since the resource they're wrapping isn't a Python object. – Mark Ransom Jan 30 '23 at 18:55