首页 > 解决方案 > JNA Structure.getFieldOrder() 与声明的字段名称不匹配

问题描述

我有以下异常在 Structure 中定义 JNA 结构:

线程“main”java.lang.Error 中的异常:com.MyInterface$mine$ByReference 类上的 Structure.getFieldOrder() 返回与声明的字段名称不匹配的名称 ([color, data, hello, rice, str, wild])

查看我的 Cpp 结构:

typedef struct s_mine
{
    e_color           color;    //e_color is enum type made of int values
    his               data;        
    int               str;        
    unsigned int      wild;         
    unsigned int      hello;        
    float             rice; 
} mine;

typedef struct s_his
{
    unsigned char * data; 
    unsigned int    size; 
} his;

请参阅下面的示例,即 MyInterface.java:

public interface MyInterface extends Library {

    public static class his extends Structure {
        public static class ByReference extends his implements Structure.ByReference {} // Need the stucture address as it a parameter of a particular wrapped method
        public static class ByValue extends his implements Structure.ByValue {}         // Need the structure value inside "mine" Structure
        public Pointer data;
        public int size;
        @Override
        protected List<String> getFieldOrder() {
            return Arrays.asList(new String[] { "data", "size"});
        }
    }
    
    public class mine extends Structure {
        public static class ByReference extends mine implements Structure.ByReference {}
        int color; 
        his.ByValue data;
        int str; 
        int wild; 
        int hello; 
        float rice;

        @Override
        protected List<String> getFieldOrder() {
            return Arrays.asList(new String[] {"color","data","str","wild","hello","rice"});
        }
    }
}

使用 MyInterface 的主类包含以下行,我对“我的”结构字段的顺序有例外:

final MyInterface.mine.ByReference mine_ref = new MyInterface.mine.ByReference();   

因此,我调试代码以便在 Structure.class 之后进入 JNA 进入以下 getFields 方法(其中我得到异常(下面的第二个):

    protected List<Field> getFields(boolean force) {
        List<Field> flist = getFieldList();
        Set<String> names = new HashSet<String>();
        for (Field f : flist) {
            names.add(f.getName());
        }

        List<String> fieldOrder = fieldOrder();
        if (fieldOrder.size() != flist.size() && flist.size() > 1) {
            if (force) {
                throw new Error("Structure.getFieldOrder() on " + getClass()
                                + (fieldOrder.size() < flist.size()
                                    ? " does not provide enough"
                                    : " provides too many")
                                + " names [" + fieldOrder.size()
                                + "] ("
                                + sort(fieldOrder)
                                + ") to match declared fields [" + flist.size()
                                + "] ("
                                + sort(names)
                                + ")");
            }
            return null;
        }

        Set<String> orderedNames = new HashSet<String>(fieldOrder);
        if (!orderedNames.equals(names)) {
            throw new Error("Structure.getFieldOrder() on " + getClass()
                            + " returns names ("
                            + sort(fieldOrder)
                            + ") which do not match declared field names ("
                            + sort(names) + ")");
        }

        sortFields(flist, fieldOrder);
        return flist;
    }

这就是我探索以下字段值的原因,因为在 getFields 方法中引发了异常

fieldOrder = [color, data, str, wild, hello, rice]
orderedNames = [str, color, data, hello, rice, wild]

引发异常是因为名称为空。无论如何,由于 HashSet java.util 类不能保证顺序会随着时间的推移保持不变,我肯定认为 JNA Structure.java 中存在错误。因为我的 fieldOrder 的顺序是按new HashSet<String>(fieldOrder)指令以随机顺序排序的,因此与orderedNames 的顺序不同。

  1. 有没有人同意我的观点,或者我错过了关于声明和使用我的 JNA 结构的方式的一些内容?

  2. 如果没有错误(即故意使用 HashSet ......)我在声明我的结构时是否做错了什么?我该如何解决这个问题?

标签: javastructurejna

解决方案


您的错误在mine结构中:您需要将字段声明为public. JNA 使用反射并依赖public修饰符来映射要映射到本机的字段;否则它们只是被认为是类中的辅助字段。

否则,映射将按照您的方式工作,但请考虑以下改进:

  • ByValue当用作嵌套结构时,您不需要使用结构的版本。这是默认设置。唯一需要对嵌套结构采取特殊操作的情况是,如果它是ByReference.
    • 同样,ByReference用作函数参数时是默认值。只有当函数需要一个ByValue参数时,它才需要特殊处理。
  • 在 JNA 5.x中,override 的使用被注释getFieldOrder()替换了。@FieldOrder虽然您所拥有的工作有效,但如果您只是将该注释放在结构之前,您的代码将更具可读性,例如,
@FieldOrder({"data", "size"})
class his extends Structure { 
  // etc... 
}
  • 接口变量/类/等。默认情况下是public, staticfinal因此使用这些修饰符是多余的。

推荐阅读