CFFI 实现因访问冲突而退出(进程已完成,退出代码为 -1073741819 (0xC0000005))

问题描述 投票:0回答:3

与 Ctypes 相比,除了语法之外,一切都相同,我的 CFFI 实现不再抛出访问冲突。

Process finished with exit code -1073741819 (0xC0000005)

我已经编写了两个单独的调用 DLL 函数的实现,它返回一个点云。 DLL 接受指向为结构分配的缓冲区的指针和缓冲区的大小。

注意:在将数据写入点云缓冲区时,DLL 仅考虑 iBufSize 参数中传递的大小,而不考虑结构中的 nPoints 变量。

from cffi import FFI

ffi = FFI()
lib = ffi.dlopen("PointCloud.dll")
ffi.cdef(
"""
typedef struct {
    double x;
    double y;
    double z;
} PointXYZ;

typedef struct {
    uint32_t nPoints;
    PointXYZ * pointcloud;
} PointCloud;

int GetPointCloud(void * ptrPointCloudBuf, int iBufSize);
"""
)

nPts = 5000000
buf_size = nPts * ffi.sizeof('PointXYZ')
pc_struct = ffi.new(
    "PointCloud *", {
        "nPoints": ffi.cast("uint32_t", 0),
        "pointcloud": ffi.new("PointXYZ []", nPts )
    }
)

retcode = lib.GetPointCloud(pc_struct, buf_size)

如果一切正常,GetPointCloud() 函数返回

retcode
SUCCESS
数据写入点云缓冲区,否则适当的错误代码到 `retcode'。

使用 Ctypes 执行相同的实现没有任何问题。

from ctypes import *

lib = cdll.LoadLibrary("PointCloud.dll")

class PointXYZ(Structure):
    _fields_ = [
        ('x', c_double),
        ('y', c_double),
        ('z', c_double)
    ]

class PointCloud(Structure):
    _fields_ = [
        ('nPoints', c_uint32),
        ('pointcloud', POINTER(PointXYZ)),
    ]

GetPointCloud = lib['GetPointCloud']
GetPointCloud.restype = c_int
GetPointCloud.argtypes = [POINTER(PointCloud), c_int]

nPts = 5000000
buf_size = nPts * sizeof(PointXYZ)
pc_struct = pointer(
            PointCloud(
                nPoints=c_uint32(0),
                pointcloud=cast((PointXYZ * nPts)(), POINTER(PointXYZ))
            )
        )

retcode = lib.GetPointCloud(pc_struct, buf_size)

注:

- DLL(纯

C
实现)源代码对我不可用。

- CFFI 和 Ctypes 的实现对于以前版本的 DLL 都运行顺利,直到它们更改了 DLL 中的某些内容,同时保持外部接口不变。

python dll ctypes cffi
3个回答
0
投票

我可以访问他们使用 DLL 的 C/C++ 演示应用程序的源代码,它运行得非常好。相应

C
代码的代码片段是:

struct PointXYZ
{
    double x;
    double y;
    double z;
};
struct PointCloud {
    uint32_t nPoints;
    PointXYZ * pointcloud;
    PointCloud() {
        pointcloud = nullptr;
        nPoints = 0;
    }
};

PointCloud pcloud;
int nPts = 5000000;

pcloud.pointcloud = new PointXYZ[nPts];
buf_size = sizeof(PointXYZ) * nPts;
int n = GetPointCloud(&pcloud, buf_size);   
if(n == 0)
{
    printf("Success, result = %d\n", n);    
}
else
{
    printf("Failure, result = %d\n", n);
}
/*
Do the rest with the point cloud.
*/

0
投票

我明白了,它是一个无效的临时引用,导致使用 Undefined Behavior(常见的陷阱,特别是对于接触 CPython 程序员)。

根据 [ReadTheDocs]:Using the ffi/lib objects - Working with pointers, structures and arrays重点是我的):

然而,这总是错误的(释放内存的使用):

p = ffi.new("char **", ffi.new("char[]", "hello, world"))
# WRONG!  as soon as p is built, the inner ffi.new() gets freed!

我准备了一个小例子

  • dll00.c:

    #include <inttypes.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    #if defined(_WIN32)
    #  define DLL00_EXPORT_API __declspec(dllexport)
    #else
    #  define DLL00_EXPORT_API
    #endif
    
    
    #if defined(__cplusplus)
    extern "C" {
    #endif
    
    DLL00_EXPORT_API int GetPointCloud(void *pBuf, int bSize);
    
    #if defined(__cplusplus)
    }
    #endif
    
    
    typedef struct {
        double x;
        double y;
        double z;
    } PointXYZ;
    
    typedef struct {
        uint32_t nPoints;
        PointXYZ *pointcloud;
    } PointCloud;
    
    
    int GetPointCloud(void *pBuf, int bSize)
    {
        if ((pBuf == NULL) || (bSize <= 0)) {
            return -1;
        }
        unsigned int seed = (unsigned int)time(NULL);
        //printf("%d %d\n", RAND_MAX, seed);
        srand(seed);
        int r = rand();
        printf("From C: Attempting to return %d points\n", r);
        size_t req = r * sizeof(PointXYZ);
        if (req > bSize) {
            return req;
        }
        PointCloud *ppc = (PointCloud*)pBuf;
        for (int i = 0; i < r; ++i)
            ppc->pointcloud[i] = (PointXYZ){i * 3, i * 3 + 1, i * 3 + 2};
        ppc->nPoints = r;
        return 0;
    }
    
  • code00.py:

    #!/usr/bin/env python
    
    import ctypes as cts
    #import random
    import sys
    
    
    DLL_NAME = "./PointCloud.{:s}".format("dll" if sys.platform[:3].lower() == "win" else "so")
    
    class PointXYZ(cts.Structure):
        _fields_ = (
            ("x", cts.c_double),
            ("y", cts.c_double),
            ("z", cts.c_double),
        )
    
    PPointXYZ = cts.POINTER(PointXYZ)
    
    
    class PointCloud(cts.Structure):
        _fields_ = (
            ("nPoints", cts.c_uint32),
            ("pointcloud", PPointXYZ),
        )
    
    PPointCloud = cts.POINTER(PointCloud)
    
    
    def call_dll_func(func, pc, size, pc_transform=lambda arg: arg):
        res = func(pc_transform(pc), size)
        print("\nFunction {:} returned: {:d}".format(func, res))
        if res != 0:
            return
        idxs = (0, pc.nPoints // 2, pc.nPoints - 1)
        print("\nPoints: {:d}\nData ({:d} points):".format(pc.nPoints, len(idxs)))
        for i in idxs:
            p = pc.pointcloud[i]
            print("  {:d}: {:.1f} {:.1f} {:.1f}".format(i, p.x, p.y, p.z))
    
    
    def main(*argv):
        #random.seed()
        dll = cts.CDLL(DLL_NAME)
        GetPointCloud = dll.GetPointCloud
        GetPointCloud.argtypes = ()
        GetPointCloud.restype = cts.c_int
    
        pts = 5000000
        buf_size = pts * cts.sizeof(PointXYZ)
        pc = PointCloud(nPoints=cts.c_uint32(0), pointcloud=cts.cast((PointXYZ * pts)(), PPointXYZ))
    
        call_dll_func(GetPointCloud, pc, buf_size, pc_transform=lambda arg: cts.byref(arg))
    
    
    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)
    
  • code01.py:

    #!/usr/bin/env python
    
    import sys
    
    from cffi import FFI
    
    from code00 import DLL_NAME, \
        call_dll_func
    
    
    ffi = FFI()
    
    ffi.cdef(
    """
    typedef struct {
        double x;
        double y;
        double z;
    } PointXYZ;
    
    typedef struct {
        uint32_t nPoints;
        PointXYZ * pointcloud;
    } PointCloud;
    
    int GetPointCloud(void *pBuf, int bSize);
    """
    )
    
    
    def main(*argv):
        dll = ffi.dlopen(DLL_NAME)
    
        pts = 5000000
        buf_size = pts * ffi.sizeof("PointXYZ")
        pc_buf = ffi.new("PointXYZ []", pts)  # @TODO - cfati: - Take it outside
        pc = ffi.new(
            "PointCloud *", {
                "nPoints": ffi.cast("uint32_t", 0),
                "pointcloud": pc_buf,
            }
        )
    
        GetPointCloud = dll.GetPointCloud
    
        call_dll_func(GetPointCloud, pc, buf_size)
    
    
    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\q076053999]> sopr.bat
### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###

[prompt]> "c:\Install\pc032\Microsoft\VisualStudioCommunity\2019\VC\Auxiliary\Build\vcvarsall.bat" x64 > nul

[prompt]>
[prompt]> dir /b
code00.py
code01.py
dll00.c

[prompt]>
[prompt]> cl /nologo /MD /DDLL dll00.c  /link /NOLOGO /DLL /OUT:PointCloud.dll
dll00.c
   Creating library PointCloud.lib and object PointCloud.exp

[prompt]>
[prompt]> dir /b
code00.py
code01.py
dll00.c
dll00.obj
PointCloud.dll
PointCloud.exp
PointCloud.lib

[prompt]>
[prompt]> :: CTypes
[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.10_test0\Scripts\python.exe" ./code00.py
Python 3.10.9 (tags/v3.10.9:1dd9be6, Dec  6 2022, 20:01:21) [MSC v.1934 64 bit (AMD64)] 064bit on win32

From C: Attempting to return 9724 points

Function <_FuncPtr object at 0x0000020CA1C40EE0> returned: 0

Points: 9724
Data (3 points):
  0: 0.0 1.0 2.0
  4862: 14586.0 14587.0 14588.0
  9723: 29169.0 29170.0 29171.0

Done.


[prompt]>
[prompt]> :: CFFI
[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.10_test0\Scripts\python.exe" ./code01.py
Python 3.10.9 (tags/v3.10.9:1dd9be6, Dec  6 2022, 20:01:21) [MSC v.1934 64 bit (AMD64)] 064bit on win32

From C: Attempting to return 9740 points

Function <cdata 'int(*)(void *, int)' 0x00007FFB33351020> returned: 0

Points: 9740
Data (3 points):
  0: 0.0 1.0 2.0
  4870: 14610.0 14611.0 14612.0
  9739: 29217.0 29218.0 29219.0

Done.

可能还需要 ro 检查 [SO]:通过 ctypes 从 Python 调用的 C 函数返回不正确的值(@CristiFati 的回答) 一个(另一个)陷阱:这次使用 CTypes(调用函数)。


0
投票

@CristiFati,这是随DLL提供的头文件的内容。当然它还有一些其他的功能。但我只粘贴了我们感兴趣的部分。我确认,除此之外,没有其他内容。

#ifndef POINTCLOUD_H
#define POINTCLOUD_H
#if defined(_MSC_VER)
    #define POINTCLOUD_DLL_EXPORT __declspec(dllexport)        
#else
    #define POINTCLOUD_DLL_EXPORT __attribute__ ((visibility ("default")))
    #define _stdcall
#endif

// Point structs
typedef struct {
    double x;
    double y;
    double z;
} PointXYZ;

struct PointCloud {
    uint32_t number_of_points;
    PointXYZ * pointcloud;
    PointCloud() {
        pointcloud= nullptr;
        number_of_points = 0;
    }
};

extern "C" POINTCLOUD_DLL_EXPORT int  GetPointCloud(void* buffer, int buffer_size);

#endif // POINTCLOUD_H
© www.soinside.com 2019 - 2024. All rights reserved.