Python - 将字符串对象转换为 ctypes (unsigned char *)
Posted
技术标签:
【中文标题】Python - 将字符串对象转换为 ctypes (unsigned char *)【英文标题】:Python - converting a string object into a ctypes (unsigned char *) 【发布时间】:2013-03-11 08:21:52 【问题描述】:我想使用具有以下签名的函数的 DLL 文件 -
bool isValid = isImageValid((unsigned char *) buff, const __uint32& buffLen,
int imageW, int imageH, __uint32 requiredSize);
现在,buff 必须来自一个字符串,我得到的 python 包装器等效于
pyBuff = open(someimagefile, 'rb').read()
由于 pyBuff 很大(大部分),我不想分配一个新的 c uchar 数组并复制,而是要利用原始缓冲区。基本上,抓取 pyBuff 对象的 char 缓冲区并将其引用为 ctypes (c_ubyte *)。
我最接近它的是:
buff = cast(pyBuff, POINTER(c_ubyte))
但我不确定这是我想要的类型。无论如何,该函数给了我以下错误
WindowsError: exception: access violation writing 0x00000000
很高兴得到任何帮助..
另外 - 两个简短的问题:
下面的定义是否正确,因为在这个 DLL 的现有 C 包装器中(我正在使用它作为参考)调用(也)这个的函数具有签名:(const char * buff, const __uint32& buffLen)
byref(c_ulong(len(pyBuff))
CDLL 和 WinDLL 有什么区别,哪一种适合?
【问题讨论】:
这是 Python 2 还是 3? 2.5.必须使用这个版本 【参考方案1】:如果您只有 Python 字符串并且您确定 isImageValid
不会修改其内容,那么您可以将 buff
参数声明为类型 c_char_p
。缓冲区内容不会被复制。
c_char_p
的 _type_
代码是“z”。在 setter 函数 _ctypes/cfield.c : z_set 的 2.5.4 源代码中,您会看到对于 str
对象,它只使用底层的 ob_sval
指针:
1309 if (PyString_Check(value))
1310 *(char **)ptr = PyString_AS_STRING(value);
1311 Py_INCREF(value);
1312 return value;
其中宏定义如下:
#define PyString_AS_STRING(op) (((PyStringObject *)(op))->ob_sval)
也就是说,如果您可以读取源文件,您可能更愿意将其读入可变缓冲区。以下是如何将内容读入 ctypes c_char
数组(在 2.5.4 中测试):
创建要读取的测试文件:
>>> open('tmp.txt', 'w').write('''\
... This is a test.''')
在c_uint32
中获取文件大小,创建缓冲区,并读入文件内容:
>>> import os
>>> from ctypes import *
>>> buffLen = c_uint32()
>>> buffLen.value = os.path.getsize('tmp.txt')
>>> buff = (c_char * buffLen.value)()
>>> open('tmp.txt').readinto(buf)
15
>>> buff.value
'This is a test.'
关于你的另外两个问题,我会这样设置函数原型:
lib = CDLL(path_to_dll)
isImageValid = lib.isImageValid
isImageValid.argtypes = c_char_p, POINTER(c_uint32), c_int, c_int, c_uint32
isImageValid.restype = c_bool
ctypes 将自动通过引用传递buffLen
给定上述argtypes
定义。您也可以显式使用byref(buffLen)
。
CDLL
用于 cdecl(C 声明)调用约定,其中 调用者 清理堆栈。这允许可变参数函数,例如printf
。 WinDLL
用于 stdcall 约定,其中 callee 清理堆栈。 stdcall 主要由 32 位 Win32 API 使用,例如 Kernel32 函数。请参阅x86 calling conventions 上的***文章。
【讨论】:
非常感谢。我可能不清楚这一点,但我无权访问文件本身,只能访问读取后的字符串对象。我必须创建 C 数组并进行复制,还是可以使用已经为 Py 字符串分配的缓冲区?对于其他一切,非常感谢。 @UriElias:我用有关c_char_p
工作原理的更多信息修改了我的答案。我希望它有所帮助。以上是关于Python - 将字符串对象转换为 ctypes (unsigned char *)的主要内容,如果未能解决你的问题,请参考以下文章
ctypes - 将 c++ 函数的输出转换为 python 对象
如何将 ctypes 的 c_long 转换为 Python 的 int?
[转] python关于ctypes使用char指针与bytes相互转换的问题