【问题标题】:How do I copy a part of bytearray to c_void_p-referenced memory and vice versa?如何将字节数组的一部分复制到 c_void_p 引用的内存,反之亦然?
【发布时间】:2019-12-09 19:35:14
【问题描述】:

在 Python 应用程序中,我有一个包含一些数据的字节数组。我的 python 代码被一些外部本机库 (DLL/so/dylib) 调用(使用 ctypes 及其callback functions)。其中一个回调函数包括一个指向非托管缓冲区的指针,该缓冲区在该外部库中分配。

我需要将字节数组的一部分复制到这个非托管缓冲区或将非托管缓冲区的内容复制到特定位置的字节数组中。

简单地说,我有

def copy_from_python_callback(c_void_p_parameter : c_void_p, offset : int, size : int):
  managed_buf = bytearray(some_size) # suppose we have data in this buffer already
  unmanaged_buf = c_void_p_parameter
  # what do I need to do here?
  # src_buf = pointer_to_specific_byte_in_managed_buf
  memmove(unmanaged_buf, src_buf, size)

def copy_to_python_callback(c_void_p_parameter : c_void_p, offset : int, size : int):
  managed_buf = bytearray(some_size) #some_size is assumed to be larger than offset + size
  unmanaged_buf = c_void_p_parameter
  # what do I need to do here?
  # dst_buf = pointer_to_specific_byte_in_managed_buf
  memmove(dst_buf, unmanaged_buf, size)

在其他语言中,答案很简单——我要么调用专用方法(例如在 .NET Framework 的 Marshal 类中),要么获取指向 bytearray 中特定字节的指针(在 C++ 或 Pascal 等本地语言中) ) 并完成。不幸的是,如果没有中间 bytes() 或类似的缓冲区,我看不到如何在 Python 中执行这些操作。

我有一些使用中间 bytes() 实例的方法,但是仅仅因为无法获取指针而复制数据对我来说似乎很奇怪。

如果可能的话,我正在寻找与版本无关的解决方案,但也可以使用仅 python3 的解决方案。先感谢您。

【问题讨论】:

    标签: python ctypes


    【解决方案1】:

    上市[Python 3.Docs]: ctypes - A foreign function library for Python

    您正在寻找的是可能的。数组 (CTypes) 派上用场。
    下面是一个“小”示例。出于演示目的,缓冲区仅包含“人类友好”chars。

    dll00.c

    #include <stdio.h>
    #include <stdint.h>
    #include <stdlib.h>
    
    #if defined(_WIN32)
    #  define DLL00_EXPORT_API __declspec(dllexport)
    #else
    #  define DLL00_EXPORT_API
    #endif
    
    #define C_TAG "From C - "
    
    
    typedef int (*ReadFunc)(void *ptr, uint32_t offset, uint32_t size);
    typedef int (*WriteFunc)(void *ptr, uint32_t offset, uint32_t size);
    
    #if defined(__cplusplus)
    extern "C" {
    #endif
    
    DLL00_EXPORT_API int testBufferCallbacks(uint32_t bufSize,
                                             ReadFunc read, uint32_t readOffset, uint32_t readSize,
                                             WriteFunc write, uint32_t writeOffset, uint32_t writeSize);
    
    #if defined(__cplusplus)
    }
    #endif
    
    
    void prinBuffer(const uint8_t *buf, uint32_t size) {
        printf("%sBuffer (size %d) 0x%016llX\n  Contents: ", C_TAG, size, (uint64_t)buf);
        for (uint32_t i = 0; i < size; i++) {
            printf("%c", ((uint8_t*)buf)[i]);
        }
        printf("\n");
    }
    
    int testBufferCallbacks(uint32_t bufSize,
                            ReadFunc read, uint32_t readOffset, uint32_t readSize,
                            WriteFunc write, uint32_t writeOffset, uint32_t writeSize) {
        const uint8_t charOffset = 0x41;
        void *buf = malloc(bufSize);
        uint8_t *cBuf = (uint8_t*)buf;
        for (uint32_t i = 0; i < bufSize; i++) {
            cBuf[i] = (uint8_t)((i + charOffset) % 0x100);
        }
        prinBuffer(cBuf, bufSize);
        if ((read != NULL) && (readSize > 0)) {
            printf("\n%sCalling read(0x%016llX, %u, %u)...\n", C_TAG, (uint64_t)buf, readOffset, readSize);
            read(buf, readOffset, readSize);
        }
        if ((write != NULL) && (writeSize > 0)) {
            printf("\n%sCalling write(0x%016llX, %u, %u)...\n", C_TAG, (uint64_t)buf, writeOffset, writeSize);
            write(buf, writeOffset, writeSize);
            prinBuffer(cBuf, bufSize);
        }
        if ((read != NULL) && (readSize > 0)) {
            printf("\n%sCalling read(0x%016llX, %u, %u)...\n", C_TAG, (uint64_t)buf, readOffset, readSize);
            read(buf, readOffset, readSize);
        }
        prinBuffer(cBuf, bufSize);
        free(buf);
        printf("\n%sDone.\n", C_TAG);
        return 0;
    }
    

    code00.py

    #!/usr/bin/env python3
    
    import sys
    import ctypes as ct
    
    
    DLL_NAME = "./dll00.dll"
    
    ReadFunc = ct.CFUNCTYPE(ct.c_int, ct.c_void_p, ct.c_uint32, ct.c_uint32)
    WriteFunc = ct.CFUNCTYPE(ct.c_int, ct.c_void_p, ct.c_uint32, ct.c_uint32)
    
    
    def create_bytearray(size, offset_char=0x61):
        contents = "".join(chr(i) for i in range(offset_char, offset_char + size))
        return bytearray(contents.encode())
    
    
    def read_c_buf(buf : ct.c_void_p, offset : ct.c_uint32, size : ct.c_uint32):
        print("C buf: 0x{0:016X}".format(buf))
        ba = create_bytearray(0x1A)
        print("Python initial buffer: {0:}".format(ba))
        UCharArr = ct.c_uint8 * size
        uchar_arr = UCharArr.from_buffer(ba, offset)  # Shared memory
        ct.memmove(uchar_arr, buf, size)
        print("Python final buffer: {0:}\n".format(ba))
        return 0
    
    
    def write_c_buf(buf : ct.c_void_p, offset : ct.c_uint32, size : ct.c_uint32):
        print("C buf: 0x{0:016X}".format(buf))
        ba = create_bytearray(size + offset, offset_char=0x30 - offset)
        print("Python buffer: {0:}\n".format(ba))
        UCharArr = ct.c_uint8 * size
        uchar_arr = UCharArr.from_buffer(ba, offset)  # Shared memory
        ct.memmove(buf, uchar_arr, size)
        return 0
    
    
    def main(*argv):
        dll00 = ct.CDLL(DLL_NAME)
        testBufferCallbacks = dll00.testBufferCallbacks
        testBufferCallbacks.argtypes = (ct.c_uint32, ReadFunc, ct.c_uint32, ct.c_uint32, WriteFunc, ct.c_uint32, ct.c_uint32)
        testBufferCallbacks.restype = ct.c_int
    
        read_callback = ReadFunc(read_c_buf)
        buf_size = 0x1A
        read_offset = 10
        read_size = 16
        write_callback = WriteFunc(write_c_buf)
        write_offset = 5
        write_size = 10
        res = testBufferCallbacks(buf_size, read_callback, read_offset, read_size, write_callback, write_offset, write_size)
        print("\n{0:s} returned: {1:d}".format(testBufferCallbacks.__name__, res))
    
    
    if __name__ == "__main__":
        print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
        main(*sys.argv[1:])
        print("\nDone.")
    

    输出

    [cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q059255471]> sopr.bat
    *** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***
    
    [prompt]> "c:\Install\x86\Microsoft\Visual Studio Community\2017\VC\Auxiliary\Build\vcvarsall.bat" x64
    **********************************************************************
    ** Visual Studio 2017 Developer Command Prompt v15.9.17
    ** Copyright (c) 2017 Microsoft Corporation
    **********************************************************************
    [vcvarsall.bat] Environment initialized for: 'x64'
    
    [prompt]> dir /b
    code00.py
    code01.py
    dll00.c
    
    [prompt]> cl /nologo /MD /DDLL dll00.c  /link /NOLOGO /DLL /OUT:dll00.dll
    dll00.c
       Creating library dll00.lib and object dll00.exp
    
    [prompt]> dir /b
    code00.py
    code01.py
    dll00.c
    dll00.dll
    dll00.exp
    dll00.lib
    dll00.obj
    
    [prompt]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code00.py
    Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit on win32
    
    From C - Buffer (size 26) 0x0000016BFE546EB0
      Contents: ABCDEFGHIJKLMNOPQRSTUVWXYZ
    
    From C - Calling read(0x0000016BFE546EB0, 10, 16)...
    C buf: 0x0000016BFE546EB0
    Python initial buffer: bytearray(b'abcdefghijklmnopqrstuvwxyz')
    Python final buffer: bytearray(b'abcdefghijABCDEFGHIJKLMNOP')
    
    
    From C - Calling write(0x0000016BFE546EB0, 5, 10)...
    C buf: 0x0000016BFE546EB0
    Python buffer: bytearray(b'+,-./0123456789')
    
    From C - Buffer (size 26) 0x0000016BFE546EB0
      Contents: 0123456789KLMNOPQRSTUVWXYZ
    
    From C - Calling read(0x0000016BFE546EB0, 10, 16)...
    C buf: 0x0000016BFE546EB0
    Python initial buffer: bytearray(b'abcdefghijklmnopqrstuvwxyz')
    Python final buffer: bytearray(b'abcdefghij0123456789KLMNOP')
    
    From C - Buffer (size 26) 0x0000016BFE546EB0
      Contents: 0123456789KLMNOPQRSTUVWXYZ
    
    From C - Done.
    
    testBufferCallbacks returned: 0
    
    Done.
    

    不用说,超出 (C) 缓冲区的边界会产生 未定义的行为(我的代码不会执行那种检查)。

    【讨论】:

    • 非常感谢,我已经检查好了,我正在寻找类似“UCharArr = ct.c_uint8 * size \ uchar_arr = UCharArr.from_buffer(ba, offset) # Shared memory “ 部分。一旦我在代码中实施解决方案,我将接受答案。
    • 是的,当然,没问题:)
    猜你喜欢
    • 2017-11-24
    • 2010-11-10
    • 1970-01-01
    • 2011-05-18
    • 1970-01-01
    • 2019-02-14
    • 1970-01-01
    • 1970-01-01
    • 2014-01-09
    相关资源
    最近更新 更多