如何使用 TypeConverter 通过 JNA 将 C 整数映射到 Java 枚举?

Posted

技术标签:

【中文标题】如何使用 TypeConverter 通过 JNA 将 C 整数映射到 Java 枚举?【英文标题】:How to map C integer to Java enum via JNA using TypeConverter? 【发布时间】:2015-10-04 08:20:10 【问题描述】:

我希望 JNA 自动进行转换。现在我正在关注第二个答案in a very similar question 和JNA 自己的EnumConverter 实用程序类的解决方案。有一个关键的区别,我的枚举有一个构造函数参数。

我的代码定义了TypeConverter

public class SentinelStatusConverter implements TypeConverter 
    @Override
    public SentinelStatus fromNative(Object nativeValue, FromNativeContext context) 
        Integer code = (Integer) nativeValue;
        return SentinelStatus.fromCode(code);
    

    @Override
    public Integer toNative(Object value, ToNativeContext context) 
        SentinelStatus status = (SentinelStatus) value;
        return Integer.valueOf(status.getCode());
    

    @Override
    public Class<Integer> nativeType() 
        return Integer.class;
    


public class SentinelTypeMapper extends DefaultTypeMapper 
    public SentinelTypeMapper() 
        addTypeConverter(SentinelStatus.class, new SentinelStatusConverter());
    

这是直接注册本机 C 库的代码以及我的自定义 TypeMapper。 C 函数返回一个 int,我想将其自动映射到 SentinelStatus 枚举中:

public class SentinelLibrary 
    static 
        Map<String, Object> options = new HashMap<String, Object>();
        options.put(Library.OPTION_TYPE_MAPPER, new SentinelTypeMapper());
        Native.register(NativeLibrary.getInstance("libnamelib", options));
    

    public static native SentinelStatus hasp_get_sessioninfo(
        NativeLong sessionHandle,
        String query,
        PointerByReference info);

SentinelStatus 是一个enum,就像这样:

public enum SentinelStatus 
    HASP_STATUS_OK(0),
    HASP_SOME_ERROR(13),
    ...
    HASP_NOT_IMPL(1831);

    private final int code;

    SentinelStatus(final int code)  this.code = code; 

    public int getCode()  return this.code; 

    public static SentinelStatus fromCode(final int code) 
        for (SentinelStatus status : EnumSet.allOf(SentinelStatus.class)) 
            if (code == status.getCode()) 
                return status;
            
        
        return SentinelStatus.HASP_NOT_IMPL;
    

使用此 JNA 映射和转换器,每当我尝试加载 SentinelLibrary 类时都会出错:

java.lang.ExceptionInInitializerError
...
Caused by: java.lang.IllegalArgumentException: Unsupported Structure field type class package.name.SentinelStatus
at com.sun.jna.Structure$FFIType.get(Structure.java:1851)
at com.sun.jna.Structure$FFIType.get(Structure.java:1806)
at com.sun.jna.Native.register(Native.java:1438)
at com.sun.jna.Native.register(Native.java:1165)
at package.name.SentinelLibrary.<clinit>(line with Native.register() call)

我已阅读文档并且对映射的类或类型没有任何限制。只有NativeMapped 接口要求实现者提供一个公共的无参数构造函数。

是否可以通过这种方式将 C 整数映射到枚举?

更新: 在进一步翻阅 JNA 代码后,我将此字段添加到 SentinelStatus 枚举中:

public final static TypeMapper TYPE_MAPPER = new SentinelTypeMapper();

现在SentinelLibrary 已正确加载。但是所有返回枚举的方法都返回null,错误打印到stderr

JNA: unrecognized return type, size 4

【问题讨论】:

知道抛出异常的位置可能很有用。 【参考方案1】:

您很可能在库定义的上下文之外定义您的Structure(这是存储TypeMapper 信息的位置)。

您可以在您的 Structure 类上显式设置类型映射器,或者在您的本机 Library 类中定义您的 Structure 类。如果没有为Structure 明确定义TypeMapper,它将回退到任何周围的Library 类提供的选项。

public class MyStructure extends Structure 
    public MyStructure() 
        setTypeMapper(new MyTypeMapper());
    

或者,

public interface MyLibrary extends Library 
    public class MyStructure extends Structure 
    

或者直接映射:

public class MyLibrary implements Library 
     Native.register(...); 
    public class MyStructure extends Structure  

Structure 类找不到显式设置的类型映射器时,它会查找Library 类型的封闭类,并尝试查找实例化该类时使用的选项。这些选项在加载本机库时被缓存,因此可以通过使用 Library 子类作为键的键查找来获得这些选项。您需要实现Library 接口,以便您的直接映射库被识别为本机库类。

编辑

这是 JNA 的类型映射器处理中的一个错误(请参阅project issue)。这似乎仅限于直接映射库和enum 类型映射。

编辑

这里有几个 JNA 错误在起作用,如果您想尝试,请联系 fix is available。此问题与直接映射库上的类型映射器隔离,其中将原语转换为 Java 对象。

【讨论】:

感谢您的快速答复。它解释了为什么我的SentinelStatusregister 图书馆时可能无法被识别。但问题仍然存在。我想映射到的SentinelStatusenum。我真的不能让它成为Structure 的子类。 根据您的错误,SentinelStatus 被用作Structure 中的字段。需要能够找到类型映射器的是 Structure 容器。 库缓存也可能发生在Structure 查找发生之后,在这种情况下,您需要遵循第一个示例并在结构实例上显式设置类型映射器。如果您包含更多的库定义,尤其是那些包含 Structure 用法的位,将会有所帮助。 SentinelStatus enum 实际上仅用作来自SentinelLibrarynative 方法的返回类型(可以在第一个代码块的末尾看到)。我没有在任何地方使用任何Structure 子类。 显然,JNA 错误报告中有一个错误。

以上是关于如何使用 TypeConverter 通过 JNA 将 C 整数映射到 Java 枚举?的主要内容,如果未能解决你的问题,请参考以下文章

Android 房间持久库 - TypeConverter 错误错误:无法弄清楚如何将字段保存到数据库”

TypeConverter() 在 Android 中的 Room 出现 TypeConverter 错误时具有私有访问权限

通过 JNA 向隐藏窗口发送击键

java 使用JNA通过Java访问Windows注册表。

JNA调用DLL简单使用

如何使用 JNA 读取 Linux 命令的输出