Python ctypes:如何修改现有的char *数组

时间:2013-01-14 01:32:28

标签: python pointers function-pointers ctypes

我正在开发一个使用libupnp的Python应用程序,它是一个C库。我正在使用CTypes来使用这个很容易的库。我遇到的问题是我正在为读取请求注册回调函数。该函数具有以下形式的原型:

int read_callback(void *pFileHandle, char *pBuf, long nBufLength);

pFileHandle只是一些文件句柄类型。 pBuf是一个可写的内存缓冲区。这是输出数据的地方。 nBufLength是从文件中读取的字节数。返回状态代码。

我有一个Python函数指针。这很容易实现,但是当我定义一个Python函数来处理这个回调时,我发现pBuf没有写入,因为Python字符串是不可变的,当你分配或修改它们时,它们会创建新的实例。这带来了一个很大的问题,因为当函数完成所请求的文件数据时,C库需要返回char指针。由于Python字符串的方式,每次缓冲区最终都是空的。如果没有修改C库,有没有办法解决这个问题呢?

处理程序应该修改给出的缓冲区参数,这是我的问题。

所以我想要发生的是调用Python函数来执行某些文件的读取(可能在内存中,文件系统句柄或其间的任何内容)。 pBuf参数由读取流填充(再次在Python中)。然后回调返回到写入pBuf的C代码。

3 个答案:

答案 0 :(得分:4)

ctypes可以分配一个C库应该能够写入的缓冲区对象:

import ctypes
init_size = 256
pBuf = ctypes.create_string_buffer(init_size)

请参阅:http://docs.python.org/2/library/ctypes.html#ctypes.create_string_buffer

答案 1 :(得分:2)

使用pBuf和nBufLength调用回调。 pBuf已经分配了可写内存,但如果你要求pBuf.value,则会转换为不可变的python字符串。

而是将pBuf转换为可以直接修改的对象:

## If pBuf is c_char_p, then convert to writable object
c = ctypes.cast(pBuf, ctypes.POINTER(ctypes.c_char))
## At this point, you can set individual bytes
## with c[i] = x, but this is dangerous and there's a safer way:

## get address of string
addr = ctypes.addressof(c.contents)

## convert to char[] for safe writing
c2 = (c_char*nBufLength).from_address(addr)

## see how many bytes to write
nb = min(len(msg), nBufLength-1)

c2[:nb] = msg[:nb]
c2[nb+1] = '\0'

答案 2 :(得分:1)

不要将pBuf声明为c_char_pctypes将该类型转换为不可变的Python字符串。您需要将其声明为POINTER(c_char)。一个快速而肮脏的Windows示例:

DLL代码(在MSVC上编译为cl /LD test.c

typedef int (*CALLBACK)(void *pFileHandle, char *pBuf, long nBufLength);
char g_buf[10] = "012345678";
CALLBACK g_callback;
__declspec(dllexport) void set_callback(CALLBACK callback) { g_callback = callback; }
__declspec(dllexport) void call_callback() { g_callback(0,g_buf,10); }
__declspec(dllexport) const char* get_buf() { return g_buf; }

Python 3.X代码:

from ctypes import *

# Declare the callback type, argument types and return types
CALLBACK = CFUNCTYPE(c_int,c_void_p,POINTER(c_char),c_long)
dll = CDLL('x')
dll.set_callback.argtypes = [CALLBACK]
dll.set_callback.restype = None
dll.call_callback.argtypes = []
dll.call_callback.restype = None
dll.get_buf.argtypes = []
dll.get_buf.restype = c_char_p

@CALLBACK
def callback(handle,buf,length):
    for i in range(9):
        buf[i] = ord('A') + i
    buf[9] = 0
    return 0

dll.set_callback(callback)
dll.call_callback()
print(dll.get_buf())

输出:

b'ABCDEFGHI'