javadlljnajnaerator

JNA Exception in thread "main" java.lang.Error: Invalid memory access(Unknown Source)


I'm using JNA 4.0.0 to access some DLL function from Java, this DLL Native Function is declared as the following:

int ApplicationInit(HANDLE hEMV, TLV *tlv_Appl, TLV *tlv_AIP);

Types of the input parameters are described below

/* Opaque structure */
typedef void *HANDLE;

typedef struct
{
    unsigned char *_lenptr;     /* pointer to 'len' field (Private member) */
    unsigned int _len;          /* 'outer' length, specified by user (Private member) */
    unsigned short _offset;
    unsigned short len;         /* number of bytes (Public member) */

    unsigned long tag;          /* Tag tag  (Public member) */
    unsigned char *val;         /* byte string  (Public member) */
    unsigned char _tagptr[256]; /* Container for TLV data (Private member) */
} TLV;

and so, I declared it inside the library interface as follows:

public static class HANDLE extends PointerType {
        public HANDLE(Pointer address) {
            super(address);
        }
        public EMV_HANDLE() {
            super();
        }
    }

public class TLV extends Structure {
    /**
     * pointer to 'len' field (Private member)<br>
     * C type : unsigned char*
     */
    public Pointer _lenptr;
    /** 'outer' length, specified by user (Private member) */
    public int _len;
    public short _offset;
    /** number of bytes (Public member) */
    public short len;
    /** Tag tag  (Public member) */
    public NativeLong tag;
    /**
     * byte string  (Public member)<br>
     * C type : unsigned char*
     */
    public Pointer val;
    /**
     * Container for TLV data (Private member)<br>
     * C type : unsigned char[256]
     */
    public byte[] _tagptr = new byte[256];
    public TLV() {
        super();
    }
    protected List<? > getFieldOrder() {
        return Arrays.asList("_lenptr", "_len", "_offset", "len", "tag", "val", "_tagptr");
    }
    /**
     * @param _lenptr pointer to 'len' field (Private member)<br>
     * C type : unsigned char*<br>
     * @param _len 'outer' length, specified by user (Private member)<br>
     * @param len number of bytes (Public member)<br>
     * @param tag Tag tag  (Public member)<br>
     * @param val byte string  (Public member)<br>
     * C type : unsigned char*<br>
     * @param _tagptr Container for TLV data (Private member)<br>
     * C type : unsigned char[256]
     */
    public TLV(Pointer _lenptr, int _len, short _offset, short len, NativeLong tag, Pointer val, byte _tagptr[]) {
        super();
        this._lenptr = _lenptr;
        this._len = _len;
        this._offset = _offset;
        this.len = len;
        this.tag = tag;
        this.val = val;
        if ((_tagptr.length != this._tagptr.length)) 
            throw new IllegalArgumentException("Wrong array size !");
        this._tagptr = _tagptr;
    }
    public static class ByReference extends TLV implements Structure.ByReference {

    };
    public static class ByValue extends TLV implements Structure.ByValue {

    };
}

int EMV_ApplicationInit(AppdefLibrary_EMVCT.EMV_HANDLE hEMV, TLV.ByReference tlv_Appl, TLV.ByReference tlv_AIP);

and then I call it in the following way:

EMV_HANDLE hEMV= new EMV_HANDLE();
TLV.ByReference tlv_Appl=new TLV.ByReference();
TLV.ByReference tlv_AIP=new TLV.ByReference();

System.out.println(AppdefLibrary.INSTANCE.ApplicationInit(hEMV, tlv_Appl, tlv_AIP));

but I'm getting the following Exception:

Exception in thread "main" java.lang.Error: Invalid memory access
    at com.sun.jna.Native.invokeInt(Native Method)
    at com.sun.jna.Function.invoke(Function.java:383)
    at com.sun.jna.Function.invoke(Function.java:315)
    at com.sun.jna.Library$Handler.invoke(Library.java:212)
    at com.sun.proxy.$Proxy1.ApplicationInit(Unknown Source)
    at test.Test.main(Test.java:192)

Please help and thank you for your attention!


Solution

  • The Invalid Memory Access error results when you're trying to access native-side memory that you haven't properly allocated.

    Exactly how that memory gets allocated can occur a few ways and you must hunt them down to figure out your problem... you have to peel off a few layers of the onion to get to the actual problem.

    The first thing to check is your JNA type mappings. Structure sizes are notoriously at fault here. However, your structure looks correct.

    The next likely source is that you've not allocated the native-side memory for the structures themselves; this is a side effect of your choice to use ByReference to deal with the structures. If you go this route you've got a lot more overhead. However, this is all unnecessary; JNA does all the heavy lifting under the hood and when you pass a Structure as an argument to a JNA library, it really sends just the pointer to the native side... but handles the memory allocation, etc.

    You should just reference the TLV structure directly in your code.

    In the library:

    int EMV_ApplicationInit(AppdefLibrary_EMVCT.EMV_HANDLE hEMV, TLV tlv_Appl, TLV tlv_AIP);
    

    In your access code:

    TLV tlv_Appl=new TLV();
    TLV tlv_AIP=new TLV();
    
    System.out.println(AppdefLibrary.INSTANCE.ApplicationInit(hEMV, tlv_Appl, tlv_AIP));
    

    If fixing this doesn't solve your problem, another possibility is that while you've got the memory allocated for your Java structures, the C method refers internally to other memory and the API expects you to have initialized your variables in some way so that they point to (allocated) memory elsewhere. Uninitialized pointers are a likely culprit, especially if intended as "input" from the user rather than simply a callback that the function will populate. You need to carefully review the API documentation to see if this is the case and if it requires any of the pointers to be initialized.

    For example, a null HANDLE may not be acceptable to the method; it may require you to initialize the HANDLE by some other method (and later release it).

    Or, you may have needed to do something to the TLV structures to initialize their internal pointer members. The val field, for example, points to a "byte string". Does the API expect you to have allocated that already and provided the length of the memory you allocated (e.g., Pointer val = new Memory(256); tlv_Appl.val = val; tlv_Appl.len = 256;)? What exactly is the _lenptr pointing to (the comment indicates an int field but it's a char pointer, that seems odd)?

    Also, as an aside, 4.0.0 is a much older version of JNA; 4.2.2 is the current version and you should use it unless you need an older version for some other reason.

    Also as another aside, JNA already includes a mapping for WinNT.HANDLE which may be preferable to rolling your own.