javajnajnaerator

Сan't get data from native, using the ByReference to the structure


I'm a complete beginner at JNA

I am passing an empty structure to the function, which consists of structures nested in it. After calling the function, I expect the fields to be filled in, but the internal structure is always null

The native method looks like this:

SDK_API int LoadFile (char *fname, struct MyLoadFile *logfile, int reset);

The structure looks like this:

struct MyLoadFile { 
  struct Src *src
};

struct Src  {
    char *FileId;               
    unsigned long Version[4];   
    struct Msg *Messages;
    struct Src   *next;
};

My JNA function looks like this:

int LoadFile(ByteBuffer fname, MyLoadFile logfile, int reset);

I tried calling the function:

    MyLoadFile  file = new MyLoadFile ();
    byte[] bytes = "D:/test/file".getBytes(StandardCharsets.US_ASCII);//Windows OS
    ByteBuffer fName = ByteBuffer.allocateDirect(bytes.length);
    fName.put(bytes);
    fName.flip();
    System.out.println("Ret code: " + MyLibrary.LoadFile(fName, file, 0));
    **//file.src == null**

From the c++ code, I call the method as follows (and it works):

struct MyLoadFile logfile;
LoadFile ("D:/test/file", &logfile, true)

The structure classes are generated by JNAerator and look like this:

public class MyLoadFile extends Structure {
  public Src.ByReference src;
  public MyLoadFile() {
    super();
  }
  protected List<String > getFieldOrder() {
    return Arrays.asList("src");
  }

  public MyLoadFile(Src.ByReference src) {
    super();
    this.src= src;
  }
  public MyLoadFile(Pointer peer) {
    super(peer);
    read();
  }
  public static class ByReference extends MyLoadFile implements Structure.ByReference {}
  public static class ByValue extends MyLoadFile implements Structure.ByValue { }
 }

public class Src extends Structure {
    public Pointer FileId;
    public NativeLong[] Version = new NativeLong[4];
    public Msg.ByReference Messages;
    public ByReference next;

    public Src() {
      super();
    }

    protected List<String > getFieldOrder() {
      return Arrays.asList("FileId", "Version", "Messages", "next");
    }

    public Src(Pointer FileId, NativeLong Version[], Msg.ByReference Messages, 
      ByReference next) {
       super();
       this.FileId = FileId;
       if ((Version.length != this.Version.length))
           throw new IllegalArgumentException("Wrong array size !");
       this.Version = Version;
       this.Messages = Messages;
       this.next = next;
    }

    public Src(Pointer peer) {
       super(peer);
       read();
    }

    public static class ByReference extends Src implements Structure.ByReference {}
    public static class ByValue extends Src implements Structure.ByValue {}
  }

Solution

  • JNA does not automatically read the values at Structure.ByReference pointers, as there is a possibility that they may be null.

    You may be able to solve the problem by explicitly reading your MyLoadFile structure after your method call.

    MyLibrary.LoadFile(fName, file, 0);
    file.read();
    // file.src should now be populated
    

    Another solution here is to map src as a Pointer:

    @FieldOrder ({ "src" })
    public class MyLoadFile extends Structure {
      public Pointer src;
    }
    

    Then after calling LoadFile() use that pointer to instantiate the nested structure with its constructor taking a pointer argument:

    MyLibrary.LoadFile(fName, file, 0);
    Src src = new Src(file.src);
    

    You may need to do something similar to access the nested Msg.ByReference structure.