Python ctypes - how to handle arrays of strings
I'm trying to call an external library function that returns a NULL-terminated array of NULL-terminated strings.
kernel32 = ctypes.windll.kernel32
buf = ctypes.create_unicode_buffer(1024)
length = ctypes.c_int32()
if kernel32.GetVolumePathNamesForVolumeNameW(ctypes.c_wchar_p(volume),
buf, ctypes.sizeof(buf), ctypes.pointer(length)):
## ???
In other words:
buf = ctypes.create_unicode_buffer(u'Hello\0StackOverflow开发者_运维技巧\0World!\0')
How do I access all contents of buf
as a Python list? buf.value
only reaches up to the first NULL.
In C it would be something like this:
while (*sz) {;
doStuff(sz);
sz += lstrlen(sz) + 1;
}
After discovering ctypes.wstring_at()
and ctypes.addressof()
, I got this:
def wszarray_to_list(array):
offset = 0
while offset < ctypes.sizeof(array):
sz = ctypes.wstring_at(ctypes.addressof(array) + offset*2)
if sz:
yield sz
offset += len(sz)+1
else:
break
It would be easier if you posted runnable code: getting a suitable volume name for this call is a bit of a pain. buf
is an array containing length
characters. The last two characters are nulls, so ignore them, convert the array to a string using ''.join()
and split on the null characters.
import ctypes
kernel32 = ctypes.windll.kernel32
def volumes():
buf = ctypes.create_unicode_buffer(1024)
length = ctypes.c_int32()
handle = kernel32.FindFirstVolumeW(buf, ctypes.sizeof(buf))
if handle:
yield buf.value
while kernel32.FindNextVolumeW(handle, buf, ctypes.sizeof(buf)):
yield buf.value
kernel32.FindVolumeClose(handle)
def VolumePathNames(volume):
buf = ctypes.create_unicode_buffer(1024)
length = ctypes.c_int32()
kernel32.GetVolumePathNamesForVolumeNameW(ctypes.c_wchar_p(volume),
buf, ctypes.sizeof(buf), ctypes.pointer(length))
return ''.join(buf[:length.value-2]).split('\0')
for volume in volumes():
print volume
print VolumePathNames(volume)
When I run this all the lists just contain a single name, but if you double check against length that's all they contained in returned buffer.
精彩评论