Search code examples
stringstructconstantsjna

JNA non-const String member of struct


I have a C function that expects a struct that contains a non-const String.

typedef struct _A {
  char* str;
} A;

void myFunc(A* aptr) { ... }

I've tried for a long time to pass this thing via JNA but didn't manage so far.

public class A extends Structure {
  public String str;
  protected List getFieldOrder() { ...
}

doesn't work because String will be turned into a const char* instead of char* which I need.

public class A extends Structure {
  public byte[] str;
  protected List getFieldOrder() { ...
}

doesn't work because the byte array inside a struct gets turned into contiguous memory and not a pointer.

I know I can do it by using Memory and copying the String over. But I can't imagine that this is the preferred way to do it.

I also tried something like

public class NonConstStringMember extends Structure {
  public static class ByReference extends NonConstStringMember implements Structure.ByReference {}        
  public byte[] stringMember;
  protected List getFieldOrder() { ...
}

public class A extends Structure {
  public NonConstStringMember.ByReference str;
}

but it doesn't work either, maybe because of alignment issues.

What is the preferred way to do this with JNA?


Solution

  • Assuming you want this to point to an arbitrary buffer which you may write to, use Pointer (and assign it a Memory value if you're allocating the buffer).

    You then use Pointer.getString(0) or Pointer.setString(0, s) to manipulate the buffer contents.

    EDIT Windows libraries usually have an ascii or unicode version of most functions. While in most cases you can define a single Structure for use in both cases, sometimes you need to provide a little additional handling to ensure the correct types are used. Since the two modes are rarely, if ever, used simultaneously, JNA sets up options to default to one or the other (automatically mapping to the right function suffix, and automatically mapping String to either native const char* or const wchar_t* depending on the mode).

    Use setWideString() if using unicode mode and setString() otherwise, or make some accessors on your Structure to handle that for you automatically. For example:

    class MyStructure extends Structure {
        private static final boolean ASCII = Boolean.getBoolean("w32.ascii");
        public Pointer stringBuffer = new Memory(X);
        void setStringBuffer(String s) {
            stringBuffer.setString(0, s, ASCII);
        }
        String getStringBuffer() {
            return stringBuffer.getString(0, ASCII);
        }
    }
    

    EDIT Please note that in most cases you should use a String as the field type and rely on a TypeMapper to use WString behind the scenes where appropriate (e.g. User32 and W32APIOptions.DEFAULT_OPTIONS).