我猜下面的代码应该可以工作,但事实并非如此。有人可以向我解释一下如何使用这种指针吗? 当然,这只是示例代码。我需要解决的是使用来自外部库的指针(它有自己的分配器“library_allocete_memory”,内部使用 malloc)。我需要将数据从 python 代码复制到该内存中。
import ctypes
libc = ctypes.cdll.msvcrt
data = b'test data'
size = len(data)
ptr = libc.malloc(size)
ptr = ctypes.cast(ptr, ctypes.c_char_p)
# ptr = ctypes.create_string_buffer(size) # this is working, but I cannot allocate memory this way in my case
ctypes.memmove(ptr, data, size) # access violation is thrown here
这里有两件事(可能)生成 Undefined Behavior:
根据[Python.Docs]:ctypes - 加载动态链接库(强调是我的):
注意: 通过
访问标准 C 库将使用该库的过时版本,该版本可能与 Python 使用的版本不兼容。如果可能,请使用本机 Python 功能,或者导入并使用 msvcrt 模块。cdll.msvcrt
[SO]:通过 ctypes 从 Python 调用的 C 函数返回不正确的值(@CristiFati 的答案)。这是使用 CTypes(调用函数)时的常见陷阱
这是一段有效的代码(在稍微不同的场景中)。
code00.py:
#!/usr/bin/env python
import ctypes as cts
import sys
from ctypes.util import find_msvcrt
def main(*argv):
data = b"test data"
size = len(data)
ptr = None
use_msvcrt = bool(argv) # If an argument (any value) is given
if use_msvcrt:
print(f"UCRT (found by CTypes): {find_msvcrt()}")
size += 1 # + 1 for the NUL terminating char (whoever uses the buffer might rely on it)
procexp = 0 # Enable this to inspect the (current) Python process (loaded .dlls) via Process Explorer tool
if procexp:
input("Press <Enter> to load UCRT...")
libc = cts.cdll.msvcrt
if procexp:
input("UCRT loaded, press <Enter> move forward...")
malloc = libc.malloc
malloc.argtypes = (cts.c_size_t,)
malloc.restype = cts.POINTER(cts.c_char) # Rigorously should be cts.c_void_p, but choosing this form for convenience
free = libc.free
free.argtypes = (cts.c_void_p,)
free.restype = None
ptr = libc.malloc(size)
else:
ptr = cts.create_string_buffer(size)
if ptr is None:
print("Uninitialized pointer")
return -1
print(f"Ptr: {ptr} ({type(ptr)})")
cts.memmove(ptr, data, size)
if use_msvcrt:
if (not ptr):
print("NULL pointer")
return -2
print(f"Ptr data: {b''.join(ptr[i] for i in range(size - 1))}")
free(ptr)
else:
print(f"Ptr data: {bytes(ptr)}")
if __name__ == "__main__":
print(
"Python {:s} {:03d}bit on {:s}\n".format(
" ".join(elem.strip() for elem in sys.version.split("\n")),
64 if sys.maxsize > 0x100000000 else 32,
sys.platform,
)
)
rc = main(*sys.argv[1:])
print("\nDone.\n")
sys.exit(rc)
输出:
[cfati@CFATI-5510-0:e:\Work\Dev\StackExchange\StackOverflow\q078205030]> sopr.bat ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ### [prompt]> [prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.10_test0\Scripts\python.exe" ./code00.py Python 3.10.11 (tags/v3.10.11:7d4cc5a, Apr 5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)] 064bit on win32 Ptr: <ctypes.c_char_Array_9 object at 0x00000239067AB6C0> (<class 'ctypes.c_char_Array_9'>) Ptr data: b'test data' Done. [prompt]> [prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.10_test0\Scripts\python.exe" ./code00.py dummyarg Python 3.10.11 (tags/v3.10.11:7d4cc5a, Apr 5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)] 064bit on win32 UCRT (found by CTypes): None Ptr: <ctypes.LP_c_char object at 0x000002143B88B6C0> (<class 'ctypes.LP_c_char'>) Ptr data: b'test data' Done.
注释:
这通常不会照顾#1.。它在我的场景中有效,因为 malloc 和 free 是从同一个 UCRT 实例 (cts.cdll.msvcrt) 加载的。另外,(前面提到的)UCRT实例与主实例相同(由进程自动加载),但这只是巧合,不能放心依赖。
我没有使用
WebUI,我不知道你是否可以向它传递空闲内存回调(这样传递当前 free 函数是安全的),或者甚至通知它不要释放指针并使用(推荐)cts.create_string_buffer方法