首页 > 解决方案 > ctypes在使用指向类对象的指针调用函数时出错

问题描述

我的 C++ DLL 中有两个包装函数。

该函数CreateObj()创建其他类的对象并返回指向该对象的指针 在第二个函数中DoSomething,我将先前创建的指针作为第一个参数传递,第二个参数是无符号字符的 RGB 图像 (1200, 1200,3)。

在 python 端,我加载 DLL 并希望调用两个包装函数来对我在 python 端加载的 RGB 图像进行一些处理。DoSomething在从 python调用的最后一步中,我得到了一个 OSError 。

任何帮助将不胜感激。

//The C++ library code

class Obj
{
    /*..../*
};

extern "C"
{
#ifdef BUILD_AS_DLL
    __declspec(dllexport)
#endif
    Obj* CreateObj();
    int DoSomething( Obj* ptr,  uint8_t*  t[1200][1200][]);  //ptr was created using CreateObj and second argument is an image array of unsigned char of shape [1200][1200][3]
#ifdef BUILD_AS_DLL
    __declspec(dllexport)
#endif
}

调用上述DLL的Python代码

from ctypes import *
import numpy as np
import numpy.ctypeslib as npct
import cv2
    
path = os.path.join(os.getcwd(), "mwe.dll") 
lib = ctypes.CDLL(path) 

create_func = lib.CreateObj
create_func.restype = c_void_p
obj = c_void_p(create_func())

dosomething_func = lib.DoSomething
ucharPtr = npct.ndpointer(dtype=np.uint8, 
                          ndim=3, 
                          shape = (1200,1200,3),
                          flags='CONTIGUOUS') 
dosomething_func.argtype = (c_void_p,
                            ucharPtr)
dosomething_func.restype = c_int

img = cv2.imread("path/to/image")  
img_ctype = byref(img.ctypes.data_as(c_void_p))
dosomething_func(byref(obj), img_ctype)   // ERROR on this line

我收到错误

File "mwe.py", line 24, in <module>
    res = dosomething_func(
OSError: exception: access violation reading 0xFFFFFFFFFFFFFFFF

标签: pythonpython-3.xnumpyctypes

解决方案


这是一个解决方案的最小可重现示例,演示了将一个小的 3D numpy 数组传入和传出函数:

测试.cpp:

请注意,这uint8_t* t[1200][1200][]在语法上是不正确的。变量数组维度必须是第一个,而不是最后一个,这是一个指针数组而不是 8 位数据数组。

#include <cstdint>
#include <iostream>

using namespace std;

#define API __declspec(dllexport)

class Obj
{
    int m_x;
public:
    Obj(int x): m_x(x) {}
    int get_x() const { return m_x; }
};

extern "C" {

API Obj* CreateObj() {
    return new Obj(5);
}

API int DoSomething(Obj* ptr, uint8_t t[][3][3]) {
    for(uint8_t i=0; i < 3; ++i) {
        for(uint8_t j=0; j < 3; ++j) {
            for(uint8_t k=0; k < 3; ++k) {
                cout << static_cast<uint32_t>(t[i][j][k]) << ' ';
                t[i][j][k] *= 2;
            }
            cout << endl;
        }
        cout << endl;
    }
    return ptr->get_x();
}

}

测试.py:

假设cv2.imread()返回np.array与声明类型相同的类型和形状ndpointer,您只需将其直接传递给函数。

from ctypes import *
import numpy as np
import numpy.ctypeslib as npct

lib = CDLL('./test')

class OBJ(Structure):
    _fields_ = []

ARRAY = npct.ndpointer(dtype=np.uint8,shape=(3,3,3))

lib.CreateObj.argtypes = c_int,
lib.CreateObj.restype = POINTER(OBJ)
lib.DoSomething.argtypes = POINTER(OBJ),ARRAY
lib.DoSomething.restype = c_int

arr = np.arange(27,dtype=np.uint8).reshape((3,3,3))
obj = lib.CreateObj(5)
result = lib.DoSomething(obj,arr)
print(f'result = {result}')
print(arr)

输出:

0 1 2
3 4 5
6 7 8

9 10 11
12 13 14
15 16 17

18 19 20
21 22 23
24 25 26

result = 5
[[[ 0  2  4]
  [ 6  8 10]
  [12 14 16]]

 [[18 20 22]
  [24 26 28]
  [30 32 34]]

 [[36 38 40]
  [42 44 46]
  [48 50 52]]]

推荐阅读