从 JNA 调用中读取 String[] 失败
Reading String[] from JNA call fails
我正在尝试通过 JNA 调用以下函数:
struct group * getgrnam (const char *name)
如以下所述:
http://www.gnu.org/software/libc/manual/html_node/Lookup-Group.html#Lookup-Group
根据文档,结构如下:
char *gr_name
The name of the group.
gid_t gr_gid
The group ID of the group.
char **gr_mem
A vector of pointers to the names of users in the group. Each user name is a null-terminated string, and the vector itself is terminated by a null pointer.
我创建了以下简单测试 classes
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
public class Test {
public interface CLibrary extends Library {
CLibrary INSTANCE = (CLibrary) Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"), CLibrary.class);
void printf(String format, Object... args);
public Group getgrnam(String groupName);
}
public static void main(String[] args) {
CLibrary.INSTANCE.printf("Hello, World\n");
Group group = CLibrary.INSTANCE.getgrnam(args[0]);
System.out.println(group.gr_name);
System.out.println(group.gr_mem);
}
}
和表示结构的组class:
import java.util.ArrayList;
import java.util.List;
import com.sun.jna.Structure;
public class Group extends Structure {
public String gr_name;
public int gr_gid;
public String[] gr_mem = new String[128];
@Override
protected List<String> getFieldOrder() {
List<String> fields = new ArrayList<>();
fields.add("gr_name");
fields.add("gr_gid");
fields.add("gr_mem");
return fields;
}
}
marshalling documentation of JNA 指出 char** 被转换为 String[]。
然而,当我 运行 这段代码时,我得到以下错误:
/tmp # java -cp .:jna-4.5.1.jar Test root Hello, World Exception in
thread "main" java.lang.IllegalArgumentException: Reading array of
class java.lang.String from memory not supported
at com.sun.jna.Pointer.readArray(Pointer.java:538)
at com.sun.jna.Pointer.getValue(Pointer.java:459)
at com.sun.jna.Structure.readField(Structure.java:720)
at com.sun.jna.Structure.read(Structure.java:580)
at com.sun.jna.Structure.autoRead(Structure.java:2074)
at com.sun.jna.Structure.conditionalAutoRead(Structure.java:550)
at com.sun.jna.Function.invoke(Function.java:446)
at com.sun.jna.Function.invoke(Function.java:354)
at com.sun.jna.Library$Handler.invoke(Library.java:244)
at com.sun.proxy.$Proxy0.getgrnam(Unknown Source)
at Test.main(Test.java:23)
char**的结构字段如何正确转换?
自我回答,因为我经过一番挖掘后弄明白了。
主要突破是这个工具:https://github.com/nativelibs4java/JNAerator
它从 C 结构生成 Java classes,这很有帮助!
最后正确映射组结构的Javaclass是这样的:
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.ptr.PointerByReference;
import java.util.Arrays;
import java.util.List;
/**
* <i>native declaration : line 2</i><br>
* This file was autogenerated by <a href="http://jnaerator.googlecode.com/">JNAerator</a>,<br>
* a tool written by <a href="http://ochafik.com/">Olivier Chafik</a> that <a href="http://code.google.com/p/jnaerator/wiki/CreditsAndLicense">uses a few opensource projects.</a>.<br>
* For help, please visit <a href="http://nativelibs4java.googlecode.com/">NativeLibs4Java</a> , <a href="http://rococoa.dev.java.net/">Rococoa</a>, or <a href="http://jna.dev.java.net/">JNA</a>.
*/
public class Group extends Structure {
/**
* Group name.<br>
* C type : char*
*/
public Pointer gr_name;
/**
* Password.<br>
* C type : char*
*/
public Pointer gr_passwd;
/**
* Group ID.<br>
* C type : __gid_t
*/
public int gr_gid;
/**
* Member list.<br>
* C type : char**
*/
public PointerByReference gr_mem;
public Group() {
super();
}
protected List<String> getFieldOrder() {
return Arrays.asList("gr_name", "gr_passwd", "gr_gid", "gr_mem");
}
/**
* @param gr_name Group name.<br>
* C type : char*<br>
* @param gr_passwd Password.<br>
* C type : char*<br>
* @param gr_gid Group ID.<br>
* C type : __gid_t<br>
* @param gr_mem Member list.<br>
* C type : char**
*/
public Group(Pointer gr_name, Pointer gr_passwd, int gr_gid, PointerByReference gr_mem) {
super();
this.gr_name = gr_name;
this.gr_passwd = gr_passwd;
this.gr_gid = gr_gid;
this.gr_mem = gr_mem;
}
public Group(Pointer peer) {
super(peer);
}
protected ByReference newByReference() { return new ByReference(); }
protected ByValue newByValue() { return new ByValue(); }
protected Group newInstance() { return new Group(); }
// public static Group[] newArray(int arrayLength) {
// return Structure.newArray(Group.class, arrayLength);
// }
public static class ByReference extends Group implements Structure.ByReference {
};
public static class ByValue extends Group implements Structure.ByValue {
};
}
对 gr_mem 字段(C 中的 char** 类型)使用 PointerByReference 是一个突破。
之后可以这样读:
public static void main(String[] args) {
Group group = CLibrary.INSTANCE.getgrnam(args[0]);
System.out.println(group.gr_name.getString(0));
PointerByReference pbr = group.gr_mem;
String[] groups = pbr.getPointer().getStringArray(0);
for (String g : groups) {
System.out.println(g);
}
我正在尝试通过 JNA 调用以下函数:
struct group * getgrnam (const char *name)
如以下所述: http://www.gnu.org/software/libc/manual/html_node/Lookup-Group.html#Lookup-Group
根据文档,结构如下:
char *gr_name
The name of the group.
gid_t gr_gid
The group ID of the group.
char **gr_mem
A vector of pointers to the names of users in the group. Each user name is a null-terminated string, and the vector itself is terminated by a null pointer.
我创建了以下简单测试 classes
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
public class Test {
public interface CLibrary extends Library {
CLibrary INSTANCE = (CLibrary) Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"), CLibrary.class);
void printf(String format, Object... args);
public Group getgrnam(String groupName);
}
public static void main(String[] args) {
CLibrary.INSTANCE.printf("Hello, World\n");
Group group = CLibrary.INSTANCE.getgrnam(args[0]);
System.out.println(group.gr_name);
System.out.println(group.gr_mem);
}
}
和表示结构的组class:
import java.util.ArrayList;
import java.util.List;
import com.sun.jna.Structure;
public class Group extends Structure {
public String gr_name;
public int gr_gid;
public String[] gr_mem = new String[128];
@Override
protected List<String> getFieldOrder() {
List<String> fields = new ArrayList<>();
fields.add("gr_name");
fields.add("gr_gid");
fields.add("gr_mem");
return fields;
}
}
marshalling documentation of JNA 指出 char** 被转换为 String[]。
然而,当我 运行 这段代码时,我得到以下错误:
/tmp # java -cp .:jna-4.5.1.jar Test root Hello, World Exception in thread "main" java.lang.IllegalArgumentException: Reading array of class java.lang.String from memory not supported at com.sun.jna.Pointer.readArray(Pointer.java:538) at com.sun.jna.Pointer.getValue(Pointer.java:459) at com.sun.jna.Structure.readField(Structure.java:720) at com.sun.jna.Structure.read(Structure.java:580) at com.sun.jna.Structure.autoRead(Structure.java:2074) at com.sun.jna.Structure.conditionalAutoRead(Structure.java:550) at com.sun.jna.Function.invoke(Function.java:446) at com.sun.jna.Function.invoke(Function.java:354) at com.sun.jna.Library$Handler.invoke(Library.java:244) at com.sun.proxy.$Proxy0.getgrnam(Unknown Source) at Test.main(Test.java:23)
char**的结构字段如何正确转换?
自我回答,因为我经过一番挖掘后弄明白了。
主要突破是这个工具:https://github.com/nativelibs4java/JNAerator
它从 C 结构生成 Java classes,这很有帮助!
最后正确映射组结构的Javaclass是这样的:
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.ptr.PointerByReference;
import java.util.Arrays;
import java.util.List;
/**
* <i>native declaration : line 2</i><br>
* This file was autogenerated by <a href="http://jnaerator.googlecode.com/">JNAerator</a>,<br>
* a tool written by <a href="http://ochafik.com/">Olivier Chafik</a> that <a href="http://code.google.com/p/jnaerator/wiki/CreditsAndLicense">uses a few opensource projects.</a>.<br>
* For help, please visit <a href="http://nativelibs4java.googlecode.com/">NativeLibs4Java</a> , <a href="http://rococoa.dev.java.net/">Rococoa</a>, or <a href="http://jna.dev.java.net/">JNA</a>.
*/
public class Group extends Structure {
/**
* Group name.<br>
* C type : char*
*/
public Pointer gr_name;
/**
* Password.<br>
* C type : char*
*/
public Pointer gr_passwd;
/**
* Group ID.<br>
* C type : __gid_t
*/
public int gr_gid;
/**
* Member list.<br>
* C type : char**
*/
public PointerByReference gr_mem;
public Group() {
super();
}
protected List<String> getFieldOrder() {
return Arrays.asList("gr_name", "gr_passwd", "gr_gid", "gr_mem");
}
/**
* @param gr_name Group name.<br>
* C type : char*<br>
* @param gr_passwd Password.<br>
* C type : char*<br>
* @param gr_gid Group ID.<br>
* C type : __gid_t<br>
* @param gr_mem Member list.<br>
* C type : char**
*/
public Group(Pointer gr_name, Pointer gr_passwd, int gr_gid, PointerByReference gr_mem) {
super();
this.gr_name = gr_name;
this.gr_passwd = gr_passwd;
this.gr_gid = gr_gid;
this.gr_mem = gr_mem;
}
public Group(Pointer peer) {
super(peer);
}
protected ByReference newByReference() { return new ByReference(); }
protected ByValue newByValue() { return new ByValue(); }
protected Group newInstance() { return new Group(); }
// public static Group[] newArray(int arrayLength) {
// return Structure.newArray(Group.class, arrayLength);
// }
public static class ByReference extends Group implements Structure.ByReference {
};
public static class ByValue extends Group implements Structure.ByValue {
};
}
对 gr_mem 字段(C 中的 char** 类型)使用 PointerByReference 是一个突破。
之后可以这样读:
public static void main(String[] args) {
Group group = CLibrary.INSTANCE.getgrnam(args[0]);
System.out.println(group.gr_name.getString(0));
PointerByReference pbr = group.gr_mem;
String[] groups = pbr.getPointer().getStringArray(0);
for (String g : groups) {
System.out.println(g);
}