【问题标题】:Reading String[] from JNA call fails从 JNA 调用中读取 String[] 失败
【发布时间】:2018-12-18 05:31:03
【问题描述】:

我正在尝试通过JNA 调用以下函数:

结构组 * getgrnam (const char *name)

如中所述: http://www.gnu.org/software/libc/manual/html_node/Lookup-Group.html#Lookup-Group

根据文档,结构如下:

char *gr_name

组的名称。

gid_t gr_gid

组的组 ID。

char **gr_mem

指向组中用户名称的指针向量。每个用户名都是一个以空字符结尾的字符串,向量本身以空指针结尾。

我创建了以下简单的测试类

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);

        }
    }

以及表示结构的 Group 类:

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 测试root Hello, World Exception in 线程“main”java.lang.IllegalArgumentException:读取数组 不支持内存中的类 java.lang.String 在 com.sun.jna.Pointer.readArray(Pointer.java:538) 在 com.sun.jna.Pointer.getValue(Pointer.java:459) 在 com.sun.jna.Structure.readField(Structure.java:720) 在 com.sun.jna.Structure.read(Structure.java:580) 在 com.sun.jna.Structure.autoRead(Structure.java:2074) 在 com.sun.jna.Structure.conditionalAutoRead(Structure.java:550) 在 com.sun.jna.Function.invoke(Function.java:446) 在 com.sun.jna.Function.invoke(Function.java:354) 在 com.sun.jna.Library$Handler.invoke(Library.java:244) 在 com.sun.proxy.$Proxy0.getgrnam(未知来源) 在 Test.main(Test.java:23)

char**的结构域如何正确转换?

【问题讨论】:

  • 我找到的文档仅说明group“应包括”这 3 个成员,而不是它将包含这些成员。因此,您可能必须在 C 中创建一个调用 getgrnam 并返回更明确定义的包装函数。
  • 谢谢。我猜这也将是一个问题。查看 JNL 的源代码,它似乎根本没有实现将位于结构中的 char** 转换为对象中的 String[]。我尝试了 2-3 个解决方法,但到目前为止没有成功。将继续挖掘。
  • 你能不能只得到一个Pointers 的数组,然后在每个Pointer 上使用getString()
  • @DanielWiddis 尝试过,它显示出一些进展。从某种意义上说,Pointer[] 填充了一些“数据”。不幸的是,尝试通过 getString() 甚至 getChar() 读取这些数据会导致 VM 崩溃。可能与第一个评论有关,即返回的结构可以包含比记录更多的字段。

标签: java jna


【解决方案1】:

经过一番挖掘,我自己回答了。

这个工具的重大突破:https://github.com/nativelibs4java/JNAerator

它从 C 结构生成 Java 类,这很有帮助!

最终正确映射组结构的Java类是这样的:

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);
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-11
    • 1970-01-01
    • 1970-01-01
    • 2014-12-26
    • 2015-06-10
    相关资源
    最近更新 更多