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 声明)调用约定,其中 调用者 清理堆栈。这允许可变参数函数,例如printfWinDLL 用于 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相互转换的问题

使用 ctypes 将 python dict 转换为结构

如何使用 ctypes 将 Python 列表转换为 C 数组?

在 Python ctypes 中转换为数组