p_a...@vi... writes:
> Hi,
>
> I need to read some binary data from a device, using a dll call like this:
>
> int ibrd(int device, char *rdbuf, long count)
>
> The corresponding lines in python are
>
> rdbuf=c_char_p('\x00'*buflen)
> rc=ibrd(GpibHandle,rdbuffer,c_long(bufsize))
This seems to best the most commonly frequently asked question, and
probably the most confusing edge of ctypes. Probably the tutorial
should be extended by an example doing this. But if anyone want to beat
me by writing up something and putting it into the wiki would be even
greater: <http://starship.python.net/crew/theller/moin.cgi/CtypesModule>
> However, since rdbuffer is a c_char_p, if I read it with rdbuffer.value
> it gets truncated at the first \x00 byte.
You must not use c_char_p for this. c_char_p should be treated as
immutable string pointer - if you let a function write to it you are
modifying a Python string.
> Using a c_buffer for rdbuf makes
> ctypes complaining with the following traceback:
>
> gpib.ibrd.argtypes=[c_int, c_buffer, c_long]
>
> Traceback (most recent call last):
> File "A:\laser_c.py", line 18, in ?
> gpib.ibrd.argtypes=[c_int, c_buffer, c_long]
> TypeError: item 2 in _argtypes_ is not a valid C type
>
> Then:
> gpib.ibrd.argtypes = [c_int, c_char_p, c_long]
>
> [...]
>
> rdbuf=c_buffer(bufsize)
> rc=ibrd(GpibHandle,rdbuffer,c_long(bufsize))
>
> TypeError: while constructing argument 2:
> string expected instead of c_char_Array_10240 instance
I thought I had posted an example before, but I cannot find it
anymore. Here is it again:
c:\sf\ctypes>python
Python 2.3.3 (#51, Dec 18 2003, 20:22:39) [MSC v.1200 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from ctypes import *
>>> fd = cdll.msvcrt._open("readme.txt", 0)
>>> print fd
3
>>> buf = c_buffer(32)
>>> cdll.msvcrt._read(fd, buf, sizeof(buf))
32
>>> buf.value
'ctypes is a ffi (Foreign Functio'
>>> buf.raw
'ctypes is a ffi (Foreign Functio'
>>> cdll.msvcrt._close(fd)
0
>>> cdll.msvcrt._read(fd, buf, sizeof(buf))
-1
So, if you don't specify argtypes, you can just use a c_buffer instance.
To get the contents *including* null bytes, use it's .raw attribute, to
get the contents assuming it's a null-terminated string, use the .value
attribute.
If you want to specify argtypes, use POINTER(c_char) instead of
c_char_p:
>>> buf = c_buffer(32)
>>> cdll.msvcrt._read.argtypes = (c_int, POINTER(c_char), c_int)
>>> cdll.msvcrt._read(fd, buf, 32)
32
>>>
I agree that the distinction between c_char_p, POINTER(c_char), and
c_buffer is difficult. c_buffer(...) is most certainly misnamed, it
should be named create_char_array(...) instead, so that it is not
confused with the other c_xxx types. c_buffer is not a type, it's a
factory function for arrays of c_char.
Thomas
|