取消引用c_void_p的整个数据,不仅是第一个字节 [英] dereferencing the whole Data of c_void_p not only the first byte
问题描述
我现在有一个关于Python ctypes和调用C函数的问题,困扰着我几天.我正在使用Python 3.5和ctypes包装C .dll.
i have a Question about Pythons ctypes and calling C functions bothering me for a couple days now. I'm working with Python 3.5 and ctypes to wrap a C .dll.
我有一个C函数接受 void **
作为输出参数.调用后,它应该包含一个指向图像的某些8位RGB数据的指针.
I've got a C Function accepting a void **
as an output parameter. This should contain a Pointer to some 8 Bit RGB Data of a Image after the Call.
foo(void ** bar)
我声明我的Python参数和函数调用如下:
I declare my Python parameter and function call as following:
>>bar = c_void_p()
>>foo(byref(bar))
并尝试接收数据:
>>data = c_byte(bar.value)
>>data.value
20
对于第一个像素"R"字节实际上是一个合理的值但是我没有设法达到任何超出此范围的字节.为了我的理解,我现在得到c_void_p指向(?)
which actually is a reasonable value for the first Pixels 'R'-Byte But i did not manage to reach any Byte beyond this. For my understandig I now get the value of the first Byte the c_void_p points to(?)
我也尝试过:
>>data = (c_byte*3)(bar.value)
3仅用于测试目的(已尝试并达到数千个):
3 is only for test purposes (tried and went up to couple thousands):
>>data
<__main__.c_byte_Array_3 at 0x7faca45de950> #so far so good again?
>>data[0]
20
>>data[1]
0
...
>>data[n]
0
对我来说,好像我无法再访问其他字节了?
for me it seems like i cant acces any further Bytes again?
当我尝试将 bar
定义为int指针(很多人推荐的指针)时,我得到以下信息:
when i Try to define bar
as a int Pointer (what is recommend by a lot of guys) i get the following:
>>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)
我还通过网上找到的所有东西进行了工作,包括:此,
I also worked through anything i found online including: this, this, and a couple more I cant find right now.
我们非常感谢您的帮助!
Any Help is really appreciated!
修改:python文件的完整代码:
edit: whole code of the python file:
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:
call from python to C:
def get_image(handle, imageHandle, imageData, timeout = 0):
return d.SDK_GetImage(handle, byref(imageHandle), imageData, timeout)
其他信息:
-
在代码中实际上有更多的
void **
输出参数,例如imageHandle
.但是它们没有解决任何数组问题,并且看起来还不错.它们被声明为c_void_p
,并称为byref()
.
there actually are more
void**
output parameters in the code, such asimageHandle
. But they do not address any Arrays and seem to work just fine. They are declared asc_void_p
and are calledbyref( )
.
作为每行像素的字节流存储的RGB数据.因此其尺寸为[0] x尺寸为[1] x位深度长.
the RGB Data stored as a Byte stream of Pixels of each Line. so its dimension[0] x dimension[1] x bit-depth long.
我无法提供任何C代码,因为它是共享库的一部分.
I can't provide any C Code because it is part of a shared library.
推荐答案
这是一种处理方式.另外, ctypes 的官方文档:Python 3.5]:ctypes-Python的外部函数库.
This is one way of doing things. Also, ctypes's official doc: [Python 3.5]: ctypes - A foreign function library for 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: %s\n", 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()
注释:
- 由于未提及数组分配位置( C 或 Python ),因此我选择了前一个选项(否则为 double指针(
void **
)没有多大意义).这增加了代码的复杂性(除其他外:dealloc
-避免内存泄漏).使用后一个选项将生成"更少的代码,并且不需要void **
(我仍然想知道为什么仍然需要它) -
void *
是一种通用类型,没有太多信息.这就是为什么在 C 中,为了填充各个字节,我不得不将其 cast 投射到unsigned char *
中.同样的情况适用于 Python .我在 Python 中表达void **
的方式是通过POINTER(c_void_p)
- 许多代码用于打印.包括:
-
_get_print_indexes
,仅用于选择 5 (DISPLAY_VALUES_COUNT
)等距(从索引角度看)数组中的元素 -
_print_array_values
-打印值
- Since the array allocation place (C or Python) wasn't mentioned, I chose the former option (otherwise, the double pointer (
void **
) wouldn't make much sense). This adds some complexity to the code (among other things:dealloc
- to avoid memory leaks). Using the latter option would "generate" less code, and no need forvoid**
(I'm still wondering why it's needed anyway) void*
is a generic type with not very much info. That's why in C, in order to populate the individual bytes, I had to cast it tounsigned char *
. Same thing applies to Python. The way I expressedvoid **
in Python is viaPOINTER(c_void_p)
- A lot of the code is for printing purposes. That includes:
_get_print_indexes
, which is only used to select 5 (DISPLAY_VALUES_COUNT
) equidistant (from index perspective) elements in an array_print_array_values
- prints the values
输出:
(py35x64_test) e:\Work\Dev\StackOverflow\q051981858>"c:\Install\x86\Microsoft\Visual Studio Community\2015\vc\vcvarsall.bat" x64 (py35x64_test) e:\Work\Dev\StackOverflow\q051981858>dir /b code.py dll.c (py35x64_test) e:\Work\Dev\StackOverflow\q051981858>cl /nologo dll.c /DDLL /link /DLL /OUT:dll.dll dll.c Creating library dll.lib and object dll.exp (py35x64_test) e:\Work\Dev\StackOverflow\q051981858>dir /b code.py dll.c dll.dll dll.exp dll.lib dll.obj (py35x64_test) e:\Work\Dev\StackOverflow\q051981858>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.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 :
@EDIT0:
- 稍微清理了一下代码(有些重命名了,...)
- 分开打印(添加了
_print_array_values
) - 修改了 ctypes 指针,使其更有意义.引人注意的是指针与数组之间的关系(当被另一个(外部)指针包裹时):
- 在 C 中,它们有些等效:都引用1 st (数组)元素的地址.轻松地广播一个到另一个
- 在 Python 中,数组必须用
POINTER
(如按值传递类型)进行包装,以便与等效指向指针(并从/强制转换为指针)
- Cleaned the code a bit (some renames, ...)
- Separated printing (added
_print_array_values
) - Modified the ctypes pointers to be more meaningful. What stands out and can be confusing, is the relationship between a pointer and an array (when wrapped by another (outer) pointer):
- In C they are somewhat equivalent: both reference the address of the 1st (array) element. Easily cast one to another
- In Python the array must be wrapped by
POINTER
(like a pass by value type) in order to be equivalent to the pointer (and cast from / to it)
这篇关于取消引用c_void_p的整个数据,不仅是第一个字节的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
-