Home > Software engineering >  C Dll called with ctypes returns error when I call calloc
C Dll called with ctypes returns error when I call calloc

Time:09-28

When my code calls a function from a C DLL it gets a access violation error. The function:

int* get_shader_points(int uv_x0, int uv_x1, int uv_y0, int uv_y1, float** triangles, int no_tri){
int max_no_point = ((uv_x1 - uv_x0) * (uv_y0 - uv_y1)) * 2;
int* points = (int*) calloc(NULL, sizeof(int) * max_no_point); // error here

if(points == NULL){
    return NULL;
}
int p_Id = 0;
for(int y = uv_y1; y < uv_y0; y  )
for(int x = uv_x0; x < uv_x1; x  ){
    for(int tid = 0; tid < no_tri/3; tid  = 3){
        if(isInside((int) triangles[tid][0], (int) triangles[tid][1], (int) triangles[tid 1][0], (int) triangles[tid 1][1], (int) triangles[tid 2][0], (int) triangles[tid 2][1], x, y)){
            //points[p_Id   0] = x;
            //points[p_Id   1] = y;
            //p_Id  = 2;
        }
    }
}

return points;
}

I commented out the part where it sets the values in the array, because that also gives me an OSError in python

The error: OSError: exception: access violation writing 0x0000000000002C44

Python code:

lib = WinDLL("./util.dll")
uvx0, uvx1 = -30, 30
uvy0, uvy1 = 30, -30
triangles = [(-20, 10), (-10, 20), (0, 20), (40, 40), (100, 40), (40, 100)]
ptris = ((ctypes.c_float * 2) * len(triangles))(*triangles)
print(lib.get_shader_points(uvx0, uvx1, uvy0, uvy1, ptris, len(ptris)))

Also it should be noted that I am forced to compile the c code using the distutils module from python(using msvc), if that might make the DLL not work properly

CodePudding user response:

I had to do some guessing to make a reproducible example, and changed the signature of the function called slightly. Since a variable number of points can be returned there needs to be a way to know how many points are returned. I also added a way to release the allocated memory:

test.c

// To export functions of Windows DLLs
#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

#include <stdlib.h>

// Added missing implementation...just returns true for everything...
int isInside(int x1, int y1, int x2, int y2, int x3, int y3, int x, int y) {
    return 1;
}

// float** is the wrong type for a 2D array (at least an easy one to make from Python)
// Also added output parameter for number of points returned.
API int* get_shader_points(int uv_x0, int uv_x1, int uv_y0, int uv_y1, float triangles[][2], int no_tri, int *no_point) {
    int max_no_point = ((uv_x1 - uv_x0) * (uv_y0 - uv_y1)) * 2;
    // calloc was called incorrectly, it takes a count and size.
    int* points = calloc(max_no_point, sizeof(int));
    if(points == NULL) {
        return NULL;
    }

    int p_Id = 0;
    for(int y = uv_y1; y < uv_y0; y  )
        for(int x = uv_x0; x < uv_x1; x  ) {
            for(int tid = 0; tid < no_tri/3; tid  = 3) {
                if(isInside((int) triangles[tid][0], (int) triangles[tid][1], (int) triangles[tid 1][0], (int) triangles[tid 1][1], (int) triangles[tid 2][0], (int) triangles[tid 2][1], x, y)) {
                    points[p_Id   0] = x;
                    points[p_Id   1] = y;
                    p_Id  = 2;
                }
            }
        }

    *no_point = p_Id; // return number of points
    return points;    // and the allocated array
}

API void free_points(int* points) {
    free(points);
}

test.py

import ctypes as ct

lib = ct.CDLL('./test')  # WinDLL is for 32-bit __stdcall functions
                         # although either works on 64-bit.
lib.get_shader_points.argtypes = ct.c_int, ct.c_int, ct.c_int, ct.c_int, ct.POINTER(ct.c_float * 2), ct.c_int, ct.POINTER(ct.c_int)
lib.get_shader_points.restype = ct.POINTER(ct.c_int)  # Need, or default return value is c_int.
lib.free_points.argtypes = ct.POINTER(ct.c_int), 
lib.free_points.restype = None

def get_shader_points(uvx0, uvx1, uvy0, uvy1, triangles):
    ptris = ((ct.c_float * 2) * len(triangles))(*triangles)
    no_point = ct.c_int() # storage for output parameter
    result = lib.get_shader_points(uvx0, uvx1, uvy0, uvy1, ptris, len(ptris), ct.byref(no_point))
    retval = result[:no_point.value] # slicing retrieves a list of points of the correct size
    lib.free_points(result) # free the allocation.
    return retval

uvx0, uvx1 = -3, 3  # reduced range for smaller output since isInside() returns everything right now
uvy0, uvy1 = 3, -3
triangles = [(-20, 10), (-10, 20), (0, 20), (40, 40), (100, 40), (40, 100)]

points = get_shader_points(uvx0, uvx1, uvy0, uvy1, triangles)
print(len(points))
print(points)

Output:

72
[-3, -3, -2, -3, -1, -3, 0, -3, 1, -3, 2, -3, -3, -2, -2, -2, -1, -2, 0, -2, 1, -2, 2, -2, -3, -1, -2, -1, -1, -1, 0, -1, 1, -1, 2, -1, -3, 0, -2, 0, -1, 0, 0, 0, 1, 0, 2, 0, -3, 1, -2, 1, -1, 1, 0, 1, 1, 1, 2, 1, -3, 2, -2, 2, -1, 2, 0, 2, 1, 2, 2, 2]
  • Related