pythonctypes

Python and ctypes: Passing Struct as pass by reference and getting 'null pointer access' in python?


I have a DLL that allocates memory, adding data to the structure. And returns data to the python code. But, got 'Null Pointer Access' in Python code.

C file: (getatt.c) --- generate getatt.dll file after build.

typedef struct _ATTO {
    char title[132 + 1];
    char val[132 + 1];
} ATT;

__declspec(dllexport) void get_file_attributes(char* psFileName, ATT** *StructAtt, int* iTotalAtt) {
    int j = 0;
    int iError = NONE;
    char cMessage[132 + 1] = "";
    int iNumAtt = 5;

    StructAtt = (ATT***)malloc(iNumAtt * sizeof(ATT**));
    if(StructAtt == NULL) {
        fprintf(stderr, "Memory allocation failed\n");
        return;
    }
    if(iNumAtt > 0) {
        for(i = 0; i < iNumAtt; i++) {
            StructAtt[i] = malloc(sizeof(ATT*));
            if(StructAtt[i] == NULL) {
                fprintf(stderr, "Memory allocation failed\n");
                return ;
            }

            for(j = 0; j < 1; j++) {
                StructAtt[i][j] = (ATT*)malloc(sizeof(ATT));
                if(StructAtt[i][j] == NULL) {
                    fprintf(stderr, "Memory allocation failed\n");
                    return ;
                }

                strcpy((StructAtt[i][j])->title, psStructAttInfoTitle[i]); //psStructAttInfoTitle is an array of titles
                strcpy((StructAtt[i][j])->val, psStructAttInfoVal[i]);   //psStructAttInfoVal is an array of values
                sprintf(cMessage, "%s=%s\n", StructAtt[i][j]->title, StructAtt[i][j]->val);
                //  write_output(cMessage);
            }
        }
    }

    // Display values
    for(i = 0; i < iNumAtt; i++) {
        for(j = 0; j < 1; j++) {
            sprintf(message, "%s=%s\n", (StructAtt)[i][j]->title, (StructAtt)[i][j]->val);
            write_output(message);
        }
    }

    *iTotalAtt = iNumAtt;
    return;
}

Result:

TYPE=0
SUF=C
GROUP=a
OWNER=unknown
NUMSUF=467

Also, I have a python code that access this function:

import ctypes
from ctypes import *

class PY_STRUCT(Structure):
    _fields_ = [('title', c_char * 133),
                ('value', c_char * 133)]

file_name = 'Forest23wd'
file_att_dll = CDLL('./getatt')
file_attribute_dll.get_file_attributes.argtypes = [c_char_p, POINTER(POINTER(POINTER(PY_STRUCT))),POINTER(c_int)]

file_name_str = c_char_p(file_name.encode('utf-8'))
attr_list = POINTER(POINTER(PY_STRUCT))()
total_attr = c_int(0)

file_att_dll.get_file_attributes(file_name_str, byref(attr_list), byref(total_attr))

# Accessing data
print(total_attr.value)
print(attr_list[0][0].contents.title) # ValueError: NULL pointer access...

I got total_attr is 5. This looks good. I'm expecting attr_list[0][0] would be "TYPE". But I am getting an error ValueError: NULL pointer access. Any idea where am I doing wrong? Any suggestions please.


Solution

  • You need one more level of indirection, and I don't see how attr_list[0][0].contents would've worked in your code. Since it originally was a POINTER(POINTER(PY_STRUCT)), attr_list[0][0] would be a PY_STRUCT, which doesn't have a .contents member. Anyway...

    In the C code, StructAtt (an ATT***) needs to be returned, so the function should be:

    __declspec(dllexport) void get_file_attributes(char* psFileName, ATT*** *pStructAtt, int* iTotalAtt) {
    
        ATT*** StructAtt = malloc(iNumAtt * sizeof(ATT**)); // don't cast malloc in C
    ...
        *iTotalAtt = iNumAtt;
        *pStructAtt = StructAtt;
    }
    

    Then, in Python, update .argtypes and attr_list:

    file_attribute_dll.get_file_attributes.argtypes = [c_char_p, POINTER(POINTER(POINTER(POINTER(PY_STRUCT))),POINTER(c_int)]
    ...
    attr_list = POINTER(POINTER(POINTER(PY_STRUCT)))()