Search code examples
javastructurejna

Invalid memory access with Structure containing char*


I'm trying to map a Structure with contains some char * fields using JNA. I get the following exception :

Exception in thread "main" java.lang.Error: Invalid memory access

Here is my C code :

The signature of the function called

extern "C" MATHLIBRARY_API ERR ErrorTest(); 

The C sample implementation I did as an example :

ERR sngErrorTest() {
    ERR err;
    char message[] = "My message";
    char filename[] = "My file Name";

    err.errorCode = OK;
    err.errorDetails.fileName = NULL;
    err.errorDetails.lineNumber = 1;
    err.errorDetails.message = message;
    err.errorDetails.fileName = filename;
    return err;
}

The C types definition :

typedef enum {
    OK = 0, 
    ERR_ONE,
    ERR_TWO,
    ERR_THREE,
    ERR_FOUR,
    ERR_FIVE,
    ERR_SIX,
} ERR_CODE;

typedef struct {
    char *fileName; 
    char *message; 
    int lineNumber; 
} ERR_DETAILS;

typedef struct {
    ERR_CODE errorCode; 
    ERR_DETAILS errorDetails; 
} ERR;

Here is my java code :

public interface IFunctions extends Library {
    public static class ERR_DETAILS extends Structure {
        public static class ByValue extends ERR_DETAILS implements Structure.ByValue {}
        public static class ByReference extends ERR_DETAILS implements Structure.ByReference {}
        public Pointer fileName; 
        public Pointer message; 
        public int lineNumber; 
        @Override
        protected List<String> getFieldOrder() {
            return Arrays.asList(new String[] { "fileName", "message","lineNumber"});
        }
    }
    public static class ERR_CODE {
        public static int OK = 0; 
        public static int ERR_ONE = 1;
        public static int ERR_TWO = 2;
        public static int ERR_THREE = 3;
        public static int ERR_FOUR = 4;
        public static int ERR_FIVE = 5;
        public static int ERR_SIX = 6;

    }
    
    public static class ERR extends Structure {
        public static class ByValue extends ERR implements Structure.ByValue {}
        public static class ByReference extends ERR implements Structure.ByReference {}
        public ERR_CODE errorCode;
        public ERR_DETAILS errorDetails;
        @Override
        protected List<String> getFieldOrder() {
            return Arrays.asList(new String[] { "errorCode", "errorDetails"});
        }
    }
    
    public ERR ErrorTest();
    }

Here is my java test main class :

ERR err = IFunctions.ErrorTest();
  1. I replaced Pointer by String regarding char * types but I get the same issue.
  2. Once I retrieved fileName item as a Pointer, how does I get the value which is refered by fileName? Is it err.errorDetails.fileName.getString(0); ?

Thus I'm trying to isolate each type of the structure and I created as a sample the following C function :

signature :

    extern "C" MATHLIBRARY_API ERR_CODE ErrorCode();

C implementation :

ERR_CODE ErrorCode() {
    ERR_CODE err_code;
    err_code = OK;
    return err_code;
}  

Java declaration

public interface IFunctions extends Library {
....
public ERR_CODE ErrorCode();
....
}

Call of my jna function :

ERR_CODE errCode = IFunctions.ErrorCode();

In that case I get the following error : Exception in thread "main" java.lang.IllegalArgumentException: Unsupported return type class ERR_CODE in function ErrorCode

Did I forget somethink regarding the enum type definition?


Solution

  • Finally I get the response to my issue.

    In my JNA wrapper I had to explicitly defined that my function returns a structure by Value and not a structure By reference as it is done by default by JNA. As described in JNA API regarding Structure class : "When used as a function parameter or return value, this class corresponds to struct*. When used as a field within another Structure, it corresponds to struct. The tagging interfaces Structure.ByReference and Structure.ByValue may be used to alter the default behavior."

    Thus the solution is as follow :

    public interface IFunctions extends Library {
    ...
    public ERR.ByValue ErrorTest();
    }
    

    Nothing else to change. Thanx for your help.