2
votes

A 3rd party DLL have a function that expects a pointer to pointer to structure as argument:

__declspec(dllimport) int __stdcall
SegmentImages( unsigned char* imageData, int imageWidth, int imageHeight,
               int* numOfFinger, SlapInfo** slapInfo, const char* outFilename );

It segments a "slap" image of numOfFinger fingerprints (usually 4) into individual fingerprints (outFilename + number of finger).

The definition of SlapInfo is:

struct SlapInfo {
    int fingerType; 
    Point fingerPosition[4];
    int imageQuality;
    int rotation;
    int reserved[3];
};

struct Point {
    int x;
    int y;
};

A snippet of the example in C:

unsigned char* imageData;
int imageWidth, imageHeight;
//obtain imageData, imageWidth and imageHeight from scanner...
int numOfFinger;
SlapInfo* slapInfo;

int result = SegmentImages( imageData, imageWidth, imageHeight, &numOfFinger,
                            &slapInfo, FINGER_IMG_PATH);

JNA

According to JNA FAQ, In my case I should use "Structure.ByReference[]":

void myfunc(simplestruct** data_array, int count); // use Structure.ByReference[]

So, I mapped in Java/JNA like this:

Structures

public class Point extends Structure {
    public int x;
    public int y;

    public Point(){
        super();
    }

    @Override
    protected List<String> getFieldOrder() {
        return Arrays.asList("x", "y");
    }
}


public class SlapInfo extends Structure {    
    public int fingerType;
    public Point[] fingerPosition = new Point[4];
    public int imageQuality;
    public int rotation;
    public int[] reserved = new int[3];

    public SlapInfo() {
        super();
    }

    public SlapInfo(Pointer pointer){
        super(pointer);
    }

    @Override
    protected List<String> getFieldOrder() {
        return Arrays.asList("fingerType", "fingerPosition", "imageQuality",
                "rotation", "reserved");
    }

    public static class ByReference extends SlapInfo implements Structure.ByReference { }
}

Function

int SegmentImages(byte[] imageData, int imageWidth, int imageHeight, 
                  IntByReference numOfFinger, SlapInfo.ByReference[] slapInfo,
                  String outFileName);

Usage

//imageData, width and height are with valid values
IntByReference nrOfFingers = new IntByReference();
SlapInfo.ByReference[] slapInfoArray = (SlapInfo.ByReference[]) 
                new SlapInfo.ByReference().toArray(4);
                //Usually, there are 4 fingers in a slap, but the correct
                //value should be the returned in nrOfFingers

int result = lib.SegmentImages(imageData, width, height,
                nrOfFingers, slapInfoArray, FINGER_IMG_PATH);

But with this method, I get the error:

java.lang.Error: Invalid memory access
        at com.sun.jna.Native.invokeInt(Native Method)
        at com.sun.jna.Function.invoke(Function.java:419)
        at com.sun.jna.Function.invoke(Function.java:354)
        at com.sun.jna.Library$Handler.invoke(Library.java:244)
        at com.sun.proxy.$Proxy0.SegmentImages(Unknown Source)
        at scanners.JNAScanner.segmentToFile(JNAScanner.java:366)

I also tried with:

In all the cases, I got the error "Invalid memory access".

What am I doing wrong?

1
Notice the __stdcall in the C function declaration - your library is not using the cdecl calling convention. Does your library interface extend StdCallLibrary? - cbr
Hmm, I'm attempting to reproduce the invalid memory access, but am unable to. Are you sure you've allocated a large enough slapInfoArray? But then again, the C example allocates just a pointer suggesting that the function may be replacing the pointer with an array it allocates itself. It's next to impossible to throw guesses at what may be going wrong without access to the library or its documentation. - cbr
Yes, it extends StdCallLibrary, and yes, as seen in the C example, the native code allocates the memory (there is another function to deallocate it). - Luis Cardozo
Try creating a functional prototype in C, then port that to Java. - cbr
this is a Pointer structure working sample, if your code is as this sample and didn't work you probably are with specific issues with your struct - deFreitas

1 Answers

1
votes

If SegmentImages is returning the address of one or more structures in slapInfo, you need to use PointerByReference. The actual native type is struct **, but the value you're getting back in slapInfo is a pointer.

IntegerByReference iref = new IntegerByReference();
PointerByReference pref = new PointerByReference();
int result = SegmentImages(..., iref, pref, ...);

If the structs are returned in a single block, you can do this:

SlapInfo s = new SlapInfo(pref.getValue());
SlapInfo[] slaps = s.toArray(iref.getValue());