Python ctypes - 如何处理字符串数组

3 投票
2 回答
3042 浏览
提问于 2025-04-16 00:09

我正在尝试调用一个外部库的函数,这个函数返回的是一个以NULL结尾的字符串数组

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)):
    ## ???

换句话说:

buf = ctypes.create_unicode_buffer(u'Hello\0StackOverflow\0World!\0')

我该如何将buf的所有内容作为一个Python列表来访问呢? buf.value 只能获取到第一个NULL之前的内容。

在C语言中,处理起来大概是这样的:

while (*sz) {; 
    doStuff(sz);
    sz += lstrlen(sz) + 1;
}

2 个回答

3

如果你能提供可以直接运行的代码,那就简单多了:为这个调用获取合适的卷名有点麻烦。buf 是一个包含 length 个字符的数组。最后两个字符是空字符,所以可以忽略它们,用 ''.join() 把数组转换成字符串,然后根据空字符进行分割。

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)

当我运行这个代码时,所有的列表里只包含一个名字,但如果你再检查一下长度,你会发现返回的缓冲区里就只有这些内容。

6

在发现了 ctypes.wstring_at()ctypes.addressof() 这两个函数之后,我得到了这个:

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

撰写回答