2

com.sun.jna.Structure我上班时遇到了麻烦,所以我尝试复制我找到的第一个 JNA 测试但我什至无法让它工作。

为方便起见,请在线尝试。


import com.sun.jna.Structure;
import java.util.Arrays;
import java.util.List;

public class MyClass {
    public void testSimpleSize() throws Exception {
        class TestStructure extends Structure {
            public int field;
            @Override
            protected List<String> getFieldOrder() {
                return Arrays.asList("field");
            }
        }
        Structure s = new TestStructure();
        //assertEquals("Wrong size", 4, s.size());
    }

    public static void main(String[] args)
    {
        try
        {
            new MyClass().testSimpleSize();
        }
        catch (Exception e)
        {
            System.out.println(e);
        }
    }
}

代码编译正常,但运行时我得到

Exception in thread "main" java.lang.Error: Exception reading field 'field' in class MyClass$1TestStructure
    at com.sun.jna.Structure.getFieldValue(Structure.java:639)
    at com.sun.jna.Structure.deriveLayout(Structure.java:1285)
    at com.sun.jna.Structure.calculateSize(Structure.java:1159)
    at com.sun.jna.Structure.calculateSize(Structure.java:1111)
    at com.sun.jna.Structure.allocateMemory(Structure.java:414)
    at com.sun.jna.Structure.<init>(Structure.java:205)
    at com.sun.jna.Structure.<init>(Structure.java:193)
    at com.sun.jna.Structure.<init>(Structure.java:180)
    at com.sun.jna.Structure.<init>(Structure.java:172)
    at MyClass$1TestStructure.<init>(MyClass.java:8)
    at MyClass.testSimpleSize(MyClass.java:15)
    at MyClass.main(MyClass.java:23)
Caused by: java.lang.IllegalAccessException: class com.sun.jna.Structure cannot access a member of class MyClass$1TestStructure with modifiers "public"
    at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:361)
    at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:591)
    at java.base/java.lang.reflect.Field.checkAccess(Field.java:1075)
    at java.base/java.lang.reflect.Field.get(Field.java:416)
    at com.sun.jna.Structure.getFieldValue(Structure.java:636)
    ... 11 more

我错过了什么吗?

4

2 回答 2

2

我找到了一个解决方案,它比我想象的要简单得多。

根据文件

当应用程序尝试反射性地创建实例(数组除外)、设置或获取字段或调用方法时,将引发 IllegalAccessException,但当前执行的方法无权访问指定类、字段的定义,方法或构造函数。

这意味着com.sun.jna.Structure需要访问TestStructure, 才能使用反射(它需要它来获取结构字段)。解决这个问题的最简单方法是TestStructure移出该功能并将其公开。像这样:


import com.sun.jna.Structure;
import java.util.Arrays;
import java.util.List;

public class MyClass {
    public class TestStructure extends Structure {
        public int field;
        @Override
        protected List<String> getFieldOrder() {
            return Arrays.asList("field");
        }
    }
    
    public void testSimpleSize() throws Exception {
        Structure s = new TestStructure();
        //assertEquals("Wrong size", 4, s.size());
    }

    public static void main(String[] args)
    {
        try
        {
            new MyClass().testSimpleSize();
        }
        catch (Exception e)
        {
            System.out.println(e);
        }
    }
}

可能有更好的解决方案,但这是最简单的,对我有用。

于 2021-02-15T23:19:18.793 回答
2

正如您在回答中发现的那样,问题的根本原因(如堆栈跟踪所示)是缺乏对您的类的反射访问。虽然您的回答解决了您的具体问题,但我想详细说明可能发生这种情况的更多可能原因,这可能对其他人有所帮助。

您链接到的 JNA 测试用例有效的原因是因为该测试与 Structure 类位于同一包中,这使Structure该类可以访问包私有类。在您的情况下,内部类TestStructure没有public修饰符,并且位于与com.sun.jna.Structure.

您通过将班级移动到可访问的位置解决了这个问题,这很有效。大多数情况下,在 JNA 实现中,结构是在接口中声明的(因此默认情况下是public)。

出现此错误的第二种可能性是您使用的是 Java 模块 (JPMS)。默认情况下,Java 模块不允许反射访问,因此必须在module-info.java类中显式允许它,或者使用opens your.package to com.sun.jna;(允许仅运行时反射包括私有成员)或exports your.package to com.sun.jna;(允许编译时和运行时公共访问)。虽然这不适用于您的情况(您使用的是未命名模块中的默认包),但如果您创建模块化项目,这可能是您将来可能遇到的问题。

于 2021-02-15T23:39:08.857 回答