python: ctypes, 在 python 中读取 POINTER(c_char)
python: ctypes, read POINTER(c_char) in python
我有一个 POINTER(c_char)
的 ctypes 字段(根据文档,它必须是 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.
然而,ctypes本身推荐的这种用法似乎有一个缺点,它声称是一个指向单个字符的指针,但事实并非如此,它是一个指向字节数组的指针。
如何读取 Python 中 ctypes 函数(我知道 length
)返回的数组?尝试像 foo[0:len]
一样对其进行索引,其中 foo
是 POINTER(c_char)
爆炸 TypeError: 'c_char' object is not subscriptable
我可以使用 print(foo)
或 print(foo[0])
打印字节串的第一个字符
我在想 ctypes.cast
可能会起作用,但是我不知道如何将它传递给强制转换的长度(如将地址 foo 的前 N 个字节解释为 bytes
对象)
编辑:一些代码。
所以我有一个结构:
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
我还有另一个函数 returns a POINTER(foo)
lib3 = CDLL(....so)
f = lib3.f
f.restype = POINTER(foo)
我调用f
,其中returns一个POINTER(foo)
:
ptrf = f(....)
然后我试图访问 ptrf.payload
。以下代码有效:
def get_payload(ptr_to_foo):
val = cast(ptr_to_foo.contents.payload, c_char_p).value
return val[:ptr_to_foo.contents.len]
我也是
ptrf = f(....)
get_payload(ptrf)
我想知道 get_payload
函数是否会更容易编写。
如果你真的有一个 POINTER(c_char)
类型,它是可订阅的。将来提供重现您的问题的代码:
>>> 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!'
如 [Python.Docs]: ctypes - A foreign function library for Python 所述,您必须 而不是 对二进制数据使用 c_char_p。
当然可以忽略,但可能会出现意外(字符串被悄悄截断)。
虽然可以用~5行代码来举例,但还是把全贴出来:
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:
#!/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()
输出:
[cfati@CFATI-5510-0:e:\Work\Dev\Whosebug\q055103298]> sopr.bat
*** Set shorter prompt to better fit when pasted in Whosebug (or other) pages ***
[prompt]> "c:\Install\x86\Microsoft\Visual Studio Community15\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.
我有一个 POINTER(c_char)
的 ctypes 字段(根据文档,它必须是 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.
然而,ctypes本身推荐的这种用法似乎有一个缺点,它声称是一个指向单个字符的指针,但事实并非如此,它是一个指向字节数组的指针。
如何读取 Python 中 ctypes 函数(我知道 length
)返回的数组?尝试像 foo[0:len]
一样对其进行索引,其中 foo
是 POINTER(c_char)
爆炸 TypeError: 'c_char' object is not subscriptable
我可以使用 print(foo)
或 print(foo[0])
我在想 ctypes.cast
可能会起作用,但是我不知道如何将它传递给强制转换的长度(如将地址 foo 的前 N 个字节解释为 bytes
对象)
编辑:一些代码。
所以我有一个结构:
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
我还有另一个函数 returns a POINTER(foo)
lib3 = CDLL(....so)
f = lib3.f
f.restype = POINTER(foo)
我调用f
,其中returns一个POINTER(foo)
:
ptrf = f(....)
然后我试图访问 ptrf.payload
。以下代码有效:
def get_payload(ptr_to_foo):
val = cast(ptr_to_foo.contents.payload, c_char_p).value
return val[:ptr_to_foo.contents.len]
我也是
ptrf = f(....)
get_payload(ptrf)
我想知道 get_payload
函数是否会更容易编写。
如果你真的有一个 POINTER(c_char)
类型,它是可订阅的。将来提供重现您的问题的代码:
>>> 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!'
如 [Python.Docs]: ctypes - A foreign function library for Python 所述,您必须 而不是 对二进制数据使用 c_char_p。
当然可以忽略,但可能会出现意外(字符串被悄悄截断)。
虽然可以用~5行代码来举例,但还是把全贴出来:
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:
#!/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()
输出:
[cfati@CFATI-5510-0:e:\Work\Dev\Whosebug\q055103298]> sopr.bat *** Set shorter prompt to better fit when pasted in Whosebug (or other) pages *** [prompt]> "c:\Install\x86\Microsoft\Visual Studio Community15\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.