取消引用c_void_p的整个数据,而不仅仅是第一个字节



我有一个关于 Python ctype 和调用 C 函数的问题,现在已经困扰了我几天了。我正在使用Python 3.5和ctypes来包装C.dll。

我有一个接受void **作为输出参数的 C 函数。这应该包含一个指针,指向调用后图像的某些 8 位 RGB 数据。

foo(void ** bar) 

我声明我的 Python 参数和函数调用如下:

>>bar = c_void_p()
>>foo(byref(bar))

并尝试接收数据:

>>data = c_byte(bar.value)
>>data.value
20

这实际上是第一个像素"R"字节的合理值 但是除此之外,我没有设法达到任何字节。为了我的理解,我现在得到了c_void_p指向的第一个字节的值(?

我也试过:

>>data = (c_byte*3)(bar.value)

3 仅用于测试目的(尝试并增加到几千个(:

>>data
<__main__.c_byte_Array_3 at 0x7faca45de950> #so far so good again?
>>data[0]
20
>>data[1]
0
...
>>data[n]
0

对我来说,我似乎无法再次访问任何字节?

当我尝试将bar定义为 int 指针(很多人推荐(时,我得到以下内容:

>>bar = POINTER(c_int)()
>>foo(byref(bar))
>>bar
<__main__.LP_c_int at 0x7fcf71d9d8c8>  #so far so good?
>>bar.contents
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)

我还处理了我在网上找到的任何东西,包括: 这个,这个,还有几个我现在找不到的。

任何帮助真的非常感谢!

编辑:Python文件的完整代码:

from ctypes import *
from ctypes_wrapper_dijsdk import *
init()
guids = find_cameras()
handle = c_void_p()
open_camera(guids, handle, 0)
start_acquisition(handle, 1)
imageHandle = c_void_p()
VOIDPP = POINTER(c_void_p)
imageData = VOIDPP(c_void_p())
get_image(handle, imageHandle, imageData)
byte_buf = cast(imageData.contents, POINTER(c_ubyte * (1920)))
for x in range(0, (1920)-1 ):
print("value: {:3d}".format(byte_buf.contents[x]))
dimension = (c_int*2)()
get_int_parameter(imageHandle, 0x20000103, dimension, 2)
value = c_void_p()
get_int_parameter(imageHandle, 0x20000200, value)
#from now on would be able to get the ImageData Size
print("ImageFormat = ", value.value, "see SDK.h")
release_image(imageHandle)
close_camera(handle)
exit()

从 python 调用到 C:

def get_image(handle, imageHandle, imageData, timeout = 0):
return d.SDK_GetImage(handle, byref(imageHandle), imageData, timeout)

附加信息:

  • 代码中实际上还有更多void**输出参数,例如imageHandle。但是它们不解决任何数组,似乎工作得很好。它们被声明为c_void_p,称为byref( )

  • RGB 数据存储为每行像素的字节流。 所以它的维度[0] x 维度[1] x 位深度长。

  • 我无法提供任何 C 代码,因为它是共享库的一部分。

这是一种做事方式。此外,ctypes的官方文档:[Python 3.5]:ctypes - Python 的外部函数库。

dll.c

#include <stdio.h>
#include <stdlib.h>
#if defined(_WIN32)
#  define DLL_EXPORT __declspec(dllexport)
#else
#  define DLL_EXPORT
#endif
#define C_TAG "From C"
#define PRINT_MSG_0() printf("%s - [%s] (%d) - [%s]n", C_TAG, __FILE__, __LINE__, __FUNCTION__)
#define PRINT_ERR_1S(ARG0) printf("%s: %sn", C_TAG, ARG0)

DLL_EXPORT int test(void **pptr, size_t count) {
PRINT_MSG_0();
if (!pptr) {
PRINT_ERR_1S("NULL pointer received");
return -1;
}
if (*pptr) {
PRINT_ERR_1S("Non NULL inner pointer received");
return -2;
}
unsigned char *buf = (unsigned char*)malloc(count);
for (size_t i = 0; i < count; i++) {
buf[i] = (i + 1) % 0x100;
}
*pptr = buf;
return 0;
}

DLL_EXPORT void dealloc(void **pptr) {
PRINT_MSG_0();
if ((pptr) && (*pptr)) {
free(*pptr);
*pptr = NULL;
}
}

code.py

import sys
import math
from ctypes import c_ubyte, c_int, c_size_t, c_void_p, 
POINTER, CDLL, 
cast

VoidPtrPtr = POINTER(c_void_p)
dll_dll = CDLL("./dll.dll")
test_func = dll_dll.test
test_func.argtypes = [VoidPtrPtr, c_size_t]
test_func.restype = c_int
dealloc_func = dll_dll.dealloc
dealloc_func.argtypes = [c_void_p]
DISPLAY_VALUES_COUNT = 5
FORMAT_STRING_PAT = "    idx: {{:{:d}d}} - value: {{:3d}}"

def _get_print_indexes(array_size, values_count):
if array_size <= 0 or values_count <= 1 or values_count > array_size:
raise ValueError("Invalid args")
yield 0
if array_size > 1:
if values_count > 2:
interval_size = array_size / (values_count - 1)
for idx in range(1, values_count - 1):
yield int(round(idx * interval_size))
yield array_size - 1

def _print_array_values(array, array_size, values_count=DISPLAY_VALUES_COUNT):
index_width = math.ceil(math.log10(array_size))
format_string = FORMAT_STRING_PAT.format(index_width)
for idx in _get_print_indexes(array_size, values_count):
print(format_string.format(idx, array.contents.contents[idx]))

def main():
sizes = [
10,
100,
500,
1920 * 1080 * 3,
]
for size in sizes:
UByteArr = c_ubyte * size
UByteArrPtr = POINTER(UByteArr)
UByteArrPtrPtr = POINTER(UByteArrPtr)
print("nSize: {:d}".format(size))
data = UByteArrPtrPtr(UByteArrPtr())
print("data: {:}, data.contents: {:}".format(data, data.contents))
#print(addressof(data), addressof(data.contents))
ptr = cast(data, VoidPtrPtr)
res = test_func(ptr, size)
if res < 0:
print("{:s} returned {:d}. Moving on...n".format(test_func.__name__, res))
continue
print("data: {:}, data.contents: {:}".format(data, data.contents))
_print_array_values(data, size)
dealloc_func(data)
print("data: {:}, data.contents: {:}".format(data, data.contents))

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

注释

  • 由于没有提到数组分配位置(CPython(,我选择了前一个选项(否则,双指针(void **( 没有多大意义(。这增加了代码的复杂性(其中包括:dealloc- 以避免内存泄漏(。使用后一个选项将"生成"更少的代码,并且不需要void**(我仍然想知道为什么无论如何都需要它(
  • void*是一个泛型类型,信息不多。这就是为什么在C中,为了填充单个字节,我必须将其转换为unsigned char *.同样的事情也适用于Python。我在Python中表达void **的方式是通过POINTER(c_void_p)
  • 许多代码用于打印目的。这包括:
    • _get_print_indexes,仅用于在数组中选择5(DISPLAY_VALUES_COUNT(个等距(从索引角度(元素
    • _print_array_values- 打印值

输出

(py35x64_test) e:WorkDevStackOverflowq051981858>"c:Installx86MicrosoftVisual Studio Community2015vcvcvarsall.bat" x64
(py35x64_test) e:WorkDevStackOverflowq051981858>dir /b
code.py
dll.c
(py35x64_test) e:WorkDevStackOverflowq051981858>cl /nologo dll.c /DDLL  /link /DLL /OUT:dll.dll
dll.c
Creating library dll.lib and object dll.exp
(py35x64_test) e:WorkDevStackOverflowq051981858>dir /b
code.py
dll.c
dll.dll
dll.exp
dll.lib
dll.obj
(py35x64_test) e:WorkDevStackOverflowq051981858>"e:WorkDevVEnvspy35x64_testScriptspython.exe" code.py
Python 3.5.4 (v3.5.4:3f56838, Aug  8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] on win32

Size: 10
data: <__main__.LP_LP_c_ubyte_Array_10 object at 0x000001F8189FBBC8>, data.contents: <__main__.LP_c_ubyte_Array_10 object at 0x000001F8189FBC48>
From C - [dll.c] (16) - [test]
data: <__main__.LP_LP_c_ubyte_Array_10 object at 0x000001F8189FBBC8>, data.contents: <__main__.LP_c_ubyte_Array_10 object at 0x000001F8189FBCC8>
idx: 0 - value:   1
idx: 2 - value:   3
idx: 5 - value:   6
idx: 8 - value:   9
idx: 9 - value:  10
From C - [dll.c] (35) - [dealloc]
data: <__main__.LP_LP_c_ubyte_Array_10 object at 0x000001F8189FBBC8>, data.contents: <__main__.LP_c_ubyte_Array_10 object at 0x000001F8189FBD48>
Size: 100
data: <__main__.LP_LP_c_ubyte_Array_100 object at 0x000001F8189FBCC8>, data.contents: <__main__.LP_c_ubyte_Array_100 object at 0x000001F8189FBDC8>
From C - [dll.c] (16) - [test]
data: <__main__.LP_LP_c_ubyte_Array_100 object at 0x000001F8189FBCC8>, data.contents: <__main__.LP_c_ubyte_Array_100 object at 0x000001F8189FBC48>
idx:  0 - value:   1
idx: 25 - value:  26
idx: 50 - value:  51
idx: 75 - value:  76
idx: 99 - value: 100
From C - [dll.c] (35) - [dealloc]
data: <__main__.LP_LP_c_ubyte_Array_100 object at 0x000001F8189FBCC8>, data.contents: <__main__.LP_c_ubyte_Array_100 object at 0x000001F8189FBE48>
Size: 500
data: <__main__.LP_LP_c_ubyte_Array_500 object at 0x000001F8189FBC48>, data.contents: <__main__.LP_c_ubyte_Array_500 object at 0x000001F8189FBEC8>
From C - [dll.c] (16) - [test]
data: <__main__.LP_LP_c_ubyte_Array_500 object at 0x000001F8189FBC48>, data.contents: <__main__.LP_c_ubyte_Array_500 object at 0x000001F8189FBDC8>
idx:   0 - value:   1
idx: 125 - value: 126
idx: 250 - value: 251
idx: 375 - value: 120
idx: 499 - value: 244
From C - [dll.c] (35) - [dealloc]
data: <__main__.LP_LP_c_ubyte_Array_500 object at 0x000001F8189FBC48>, data.contents: <__main__.LP_c_ubyte_Array_500 object at 0x000001F8189FBF48>
Size: 6220800
data: <__main__.LP_LP_c_ubyte_Array_6220800 object at 0x000001F8189FBDC8>, data.contents: <__main__.LP_c_ubyte_Array_6220800 object at 0x000001F818A62048>
From C - [dll.c] (16) - [test]
data: <__main__.LP_LP_c_ubyte_Array_6220800 object at 0x000001F8189FBDC8>, data.contents: <__main__.LP_c_ubyte_Array_6220800 object at 0x000001F8189FBEC8>
idx:       0 - value:   1
idx: 1555200 - value:   1
idx: 3110400 - value:   1
idx: 4665600 - value:   1
idx: 6220799 - value:   0
From C - [dll.c] (35) - [dealloc]
data: <__main__.LP_LP_c_ubyte_Array_6220800 object at 0x000001F8189FBDC8>, data.contents: <__main__.LP_c_ubyte_Array_6220800 object at 0x000001F8189FBEC8>

@EDIT0

  • 清理了一下代码(一些重命名,...
  • 分离打印(_print_array_values添加(
  • 修改了ctypes指针以使其更有意义。突出且可能令人困惑的是指针和数组之间的关系(当被另一个(外部(指针包装时(:
    • C中,它们在某种程度上是等价的:两者都引用 1st(数组(元素的地址。轻松将一个投射到另一个
    • Python中,数组必须由POINTER包装(如按值类型传递(才能等效于指针(并从/到它(
  • Lnx上成功运行相同的代码

相关内容

最新更新