简体   繁体   English

python:ctypes,在python中读取POINTER(c_char)

[英]python: ctypes, read POINTER(c_char) in python

I have a ctypes field that is a POINTER(c_char) (it had to be, per the documentation, c_char_p didn't work for my application:https://docs.python.org/3.7/library/ctypes.html#ctypes.c_char_p )我有一个 ctypes 字段,它是一个POINTER(c_char) (它必须是,根据文档, c_char_p 对我的应用程序不起作用:https ://docs.python.org/3.7/library/ctypes.html#ctypes.c_char_p )

For a general character pointer that may also point to binary data, POINTER(c_char) must be used.对于也可能指向二进制数据的通用字符指针,必须使用 POINTER(c_char)。

However, this usage recommended by ctypes itself seems to have the downside that, it claims to be a pointer to a single character, however, it isn't, it's a pointer to an array of bytes.然而,ctypes 本身推荐的这种用法似乎有一个缺点,它声称是一个指向单个字符的指针,然而,它不是,它是一个指向字节数组的指针。

How can I read the array returned by the ctypes fucntion (I know the length ) in Python?如何在 Python 中读取 ctypes 函数返回的数组(我知道length )? Trying to index it like foo[0:len] where foo is a POINTER(c_char) blows up with TypeError: 'c_char' object is not subscriptable尝试像foo[0:len]那样索引它,其中foo是一个POINTER(c_char)TypeError: 'c_char' object is not subscriptable

I can print the first character of the bytestring using either print(foo) or print(foo[0])我可以使用print(foo)print(foo[0])字节串的第一个字符

I was thinking that ctypes.cast might work, however I don't know how to pass it the length of the cast (as in interpret the first N bytes from address foo as a bytes object)我在想ctypes.cast可能会起作用,但是我不知道如何将转换的长度传递给它(如将地址 foo 的前 N ​​个字节解释为bytes对象)

EDIT: some code.编辑:一些代码。

So I have a structure:所以我有一个结构:

class foo(Structure):
_fields_ = [("state", c_int),
            ("type", c_int),
            ("len", c_int),
            ("payload", POINTER(c_char))]  # according to th following the python bytes are already unsinged https://bytes.com/topic/python/answers/695078-ctypes-unsigned-char

And I have another function that returns a POINTER(foo)我还有另一个返回POINTER(foo)函数

lib3 = CDLL(....so)
f = lib3.f
f.restype = POINTER(foo)

I call f , which returns a POINTER(foo) :我调用f ,它返回一个POINTER(foo)

ptrf = f(....)

And then I was trying to access ptrf.payload .然后我试图访问ptrf.payload The following code works:以下代码有效:

def get_payload(ptr_to_foo):

    val = cast(ptr_to_foo.contents.payload, c_char_p).value
    return val[:ptr_to_foo.contents.len]

So I do所以我做

 ptrf = f(....)
 get_payload(ptrf)

I was wondering whether the get_payload function would be written more easily.我想知道get_payload函数是否会更容易编写。

As [Python.Docs]: ctypes - A foreign function library for Python states, you must not use c_char_p with binary data.作为[Python.Docs]: ctypes - Python状态的外部函数库,您不得c_char_p与二进制数据一起使用。
Of course that can be ignored, but then surprises (string silently truncated) may occur.当然可以忽略,但可能会出现意外(字符串被静默截断)。

Although it could be exemplified in ~5 lines of code, pasting the whole thing:虽然可以用大约 5行代码来举例说明,但粘贴整个内容:

dll.c : dll.c :

#include <stdlib.h>

#if defined(_WIN32)
#  define DLL_EXPORT __declspec(dllexport)
#else
#  define DLL_EXPORT
#endif

#define LEN 5


typedef struct CharPtrWrapperTag {
    int len;
    char *data;
} CharPtrWrapper;


DLL_EXPORT CharPtrWrapper *get() {
    CharPtrWrapper *ret = malloc(sizeof(CharPtrWrapper));
    ret->len = LEN;
    ret->data = malloc(LEN * sizeof(char));
    ret->data[0] = 'A';
    ret->data[1] = 'B';
    ret->data[2] = 0;
    ret->data[3] = 'C';
    ret->data[4] = 'D';
    return ret;
}


DLL_EXPORT void release(CharPtrWrapper *pWrap) {
    if (pWrap) {
        free(pWrap->data);
        pWrap->data = NULL;
        pWrap->len = 0;
        free(pWrap);
    }
}

code.py :代码.py

#!/usr/bin/env python3

import sys
import ctypes


DLL_NAME = "./dll.dll"
CharPtr = ctypes.POINTER(ctypes.c_char)

class CharPtrWrapper(ctypes.Structure):
    _fields_ = [
        ("len", ctypes.c_int),
        ("data", CharPtr),
    ]


CharPtrWrapperPtr = ctypes.POINTER(CharPtrWrapper)


def main():
    dll = ctypes.CDLL(DLL_NAME)
    get = dll.get
    get.restype = CharPtrWrapperPtr
    release = dll.release
    release.argtypes = [CharPtrWrapperPtr]
    wrap_ptr = get()
    wrap = wrap_ptr.contents
    print("{:}\n    Len: {:d}".format(wrap, wrap.len))
    for idx in range(wrap.len):
        print("        {:d}: {:}".format(idx, wrap.data[idx]))

    s = ctypes.cast(wrap.data, ctypes.c_char_p).value[:wrap.len]
    print("\nctypes.c_char_p cast: {:}".format(s))

    CharArr = ctypes.c_char * wrap.len
    char_arr = CharArr(*wrap.data[:wrap.len])
    print("CharArr: {:}".format(char_arr.raw))
    release(wrap_ptr)
    print("\nDone.")


if __name__ == "__main__":
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    main()

Output :输出

 [cfati@CFATI-5510-0:e:\\Work\\Dev\\StackOverflow\\q055103298]> sopr.bat *** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages *** [prompt]> "c:\\Install\\x86\\Microsoft\\Visual Studio Community\\2015\\vc\\vcvarsall.bat" x64 [prompt]> dir /b code.py dll.c [prompt]> cl /nologo /DDLL /MD dll.c /link /NOLOGO /DLL /OUT:dll.dll dll.c Creating library dll.lib and object dll.exp [prompt]> dir /b code.py dll.c dll.dll dll.exp dll.lib dll.obj [prompt]> "e:\\Work\\Dev\\VEnvs\\py_064_03.06.08_test0\\Scripts\\python.exe" code.py Python 3.6.8 (tags/v3.6.8:3c6b436a57, Dec 24 2018, 00:16:47) [MSC v.1916 64 bit (AMD64)] on win32 <__main__.CharPtrWrapper object at 0x000001279250D248> Len: 5 0: b'A' 1: b'B' 2: b'\\x00' 3: b'C' 4: b'D' ctypes.c_char_p cast: b'AB' CharArr: b'AB\\x00CD' Done.

If you truly have a POINTER(c_char) type, it is subscriptable.如果你真的有一个POINTER(c_char)类型,它是可下标的。 In the future provide code that reproduces your issue:将来提供重现您的问题的代码:

>>> p = cast(create_string_buffer(b'Hello, world!'),POINTER(c_char))
>>> p
<ctypes.LP_c_char object at 0x000001C2F6B58848>
>>> p[0]
b'H'
>>> p[:14]
b'Hello, world!\x00'
>>> cast(p,c_char_p).value  # only if known to be nul-terminated
b'Hello, world!'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM