实例化 JNA 结构的子类抛出 IllegalAccessException

Instantiating subclass of JNA Structure throws IllegalAccessException

我无法让 com.sun.jna.Structure 工作,所以我尝试复制 the first JNA test I found 但我什至无法让 that 工作。

为了方便,试试online


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 MyClassTestStructure
    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 MyClassTestStructure.<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 MyClassTestStructure 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

我是不是漏掉了什么?

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

根据 documentation,

An IllegalAccessException is thrown when an application tries to reflectively create an instance (other than an array), set or get a field, or invoke a method, but the currently executing method does not have access to the definition of the specified class, field, method or constructor.

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


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

可能有更好的解决方案,但这是最简单的并且适合我。

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

您链接到的 JNA 测试用例起作用的原因是因为测试 resides in the same package 作为结构 class,它赋予 Structure class 包私有访问权限classes。在您的情况下,内部 class TestStructure 没有 public 修饰符,并且位于与 com.sun.jna.Structure.

不同的包中

您通过将 class 移动到可访问的位置解决了问题,这很有效。在 JNA 实现中,结构通常在接口中声明(因此默认为 public)。

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