Search code examples
javac++fatal-errorjna

JNA: EXCEPTION_ACCESS_VIOLATION


I'm using a C++ DLL via JNA. I want to call the following method in Java, which writes into szVisor information that I want to read.

long FAR PASCAL DLL_GetLocalPortTS(char* szEquip,char* szVisor){
    ...
}

The Java interface implementation is the following:

public interface IE2E extends Library {
    // Instancia de la dll, carga la librería
    IE2E INSTANCE = (IE2E) Native.loadLibrary("e2e", IE2E.class);
    ...
    int GetLocalPortTS(String equip, String[] equipInfo);
}

And the method call:

String equip = "equipID";
String equipInfo = "";
String[] rEquipInfo = {equipInfo};
IE2E sdll = IE2E.INSTANCE; 

int ret = sdll.GetLocalPortTS(equip, rEquipInfo);

This execution nets me a fatal error in the JRE, but if I put both arguments as either String or String[] it doesn't. However, if I use both Strings it doesnt overwrite equipInfo and I don't get the info which I want; if I use both as arrays, the method doesn't get the equip value and doesn't operate.

Any insight on this will be welcome.


Solution

  • The problem is that the C code wants to write into szVisor, right? I guess it does something like this:

    long GetLocalPortTS(char* szEquip,char* szVisor){
        strcpy(szVisor, "I am C code result :)");
        return 0;
    }
    

    If you pass in a String from the Java side, then the memory is owned by the JVM, so writing to it causes a crash. What you need is a Memory object, which is a wrapped malloc'd bit of memory that the C code can safely write to.

    Your new JNA interface would be as follows. I've commented out the old version so you can compare:

    public interface IE2E extends Library {
        IE2E INSTANCE = (IE2E) Native.loadLibrary("e2e", IE2E.class);
        //int GetLocalPortTS(String equip, String[] equipInfo);
        int GetLocalPortTS(String equip, Memory equipInfo);
    }
    

    And the code to call it would be as follows, the 256 is a placeholder. Make sure you allocate enough to write the string to:

    String equip = "equipID";
    String equipInfo = "";
    //String[] rEquipInfo = {equipInfo};
    Memory rEquipInfo = new Memory(256);
    IE2E sdll = IE2E.INSTANCE;
    int ret = sdll.GetLocalPortTS(equip, rEquipInfo);
    

    To use the result as a String, you'd do this:

    rEquipInfo.getString(0);
    

    As the documentation says, the Memory's finalize() method automatically calls free on the malloc'd memory so there's no need to worry about memory leaks.