ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

JNA 默认类型映射全解析:Native 与 Java 类型对照、平台差异与自定义映射实战

JNA 默认类型映射全解析:Native 与 Java 类型对照、平台差异与自定义映射实战 系统编程后端【免费下载链接】jnaJava Native Access项目地址https://gitcode.com/gh_mirrors/jn/jna点击查看免费下载导读本文基于 www/Mappings.md 展开系统讲解 JNAJava Native Access中 Java 类型与原生 C 类型的默认映射规则从基础类型对照表、平台相关的尺寸差异如long、wchar_t到NativeLong等适配类型的底层实现再到通过TypeMapper、NativeMapped定制映射的高级用法。读完本文你将能准确为任何 C 函数接口选择对应的 Java 类型并能用自定义映射解决 BOOL、stdcall 命名等平台兼容问题。一、核心原则等宽映射Java primitive types (and their object equivalents) map directly to the native C type of the same size.JNA 默认映射的第一条原则是Java 原始类型及其包装类型直接映射到同尺寸的原生 C 类型。也就是说JNA 不是按“语义”而是按“位宽”来建立对应关系——只要 Java 类型与 C 类型的字节数一致就可以直接对应。这条原则的源码依据可以在 Native.getNativeSize(Class) 中看到JNA 为每个 Java 类维护一张“原生尺寸表”例如byte→1 字节、short→2 字节、int→4 字节、long→8 字节等调用前的参数装箱、返回值解包都依赖这张表。值得注意的是源码中boolean/Boolean的默认原生尺寸被硬编码为4 字节即按 32 位整数处理这正对应下表中int → boolean → BOOL的映射。二、默认类型映射对照表下表来自 www/Mappings.md列出了 JNA 默认的 Java ↔ Native 类型映射关系Native TypeSizeJava TypeCommon Windows Typeschar8-bit integerbyteBYTE, TCHARshort16-bit integershortWORDwchar_t16/32-bit charactercharTCHARint32-bit integerintDWORDintboolean valuebooleanBOOLlong32/64-bit integerNativeLongLONGlong long64-bit integerlong__int64float32-bit FPfloatdouble64-bit FPdoublechar*C stringStringLPCSTRvoid*pointerPointerLPVOID, HANDLE, LPXXX原文档还给出了两条补充规则无符号类型使用与有符号类型相同的映射例如 C 的unsigned int依然对应 Java 的intunsigned short对应short位宽不变即可C 枚举通常可与int互换枚举成员在底层就是整数值JNA 接口中直接使用int或自定义枚举类型皆可。三、关键映射背后的平台差异3.1wchar_t→char宽字符的尺寸分歧表中wchar_t标注为“16/32-bit character”。在 Windows 上wchar_t是 16 位对应 UTF-16 编码而在多数类 Unix 平台Linux、macOS上它是 32 位对应 UTF-32。JNA 在运行期通过原生代码探测实际尺寸Native.WCHAR_SIZE字段保存当前平台的wchar_t字节数见 Native.javagetNativeSize中char.class / Character.class返回的就是WCHAR_SIZE见 Native.java。因此JNA 的char参数/字段在 Java 侧始终是单个char在原生侧则按平台自动适配 2 或 4 字节这让跨平台接口声明保持统一。3.2long→NativeLongC long 的可变位宽C 的long在 64 位 Unix 系统上是 64 位在 WindowsLLP64 模型上却仍是 32 位。若直接用 Java 的long固定 64 位去映射Windows 上就会错位。JNA 为此专门设计了NativeLongpublic class NativeLong extends IntegerType { /** Size of a native long, in bytes. */ public static final int SIZE Native.LONG_SIZE; public NativeLong() { this(0); } public NativeLong(long value) { this(value, false); } public NativeLong(long value, boolean unsigned) { super(SIZE, value, unsigned); } }NativeLong.SIZE直接取自Native.LONG_SIZE运行期探测值见 Native.java。它的基类IntegerType实现了NativeMapped接口setValue根据size1/2/4/8做截断与符号处理toNative()返回对应的装箱数值fromNative()通过Klass.newInstance重建实例并回填值。使用示例public interface LibC extends Library { // C 原型: long lseek(int fd, long offset, int whence); NativeLong lseek(int fd, NativeLong offset, int whence); }IntegerType还支持构造时传入unsignedtrue表示无符号语义内部按0xFF/0xFFFF/0xFFFFFFFF掩码保留位模式适合处理unsigned long、unsigned int等场景。3.3void*→Pointer与char*→Stringchar*JNA 将其视为 C 字符串并自动映射为 JavaString负责编码/解码与\0终止符处理对应 Windows 的LPCSTR。若需要宽字符串可使用WString对应wchar_t*/LPCWSTR。void*不透明指针映射为PointerWindows 上的LPVOID、HANDLE以及各类LPXXX指针均属此类。从getNativeSize的实现可见Pointer、String、WString、Callback、Buffer在栈上统一按POINTER_SIZE指针宽度传递。四、深入源码默认映射如何被查找映射查找入口有两个Function方法参数与返回值与Structure结构体字段。以函数调用为例Function.java 对返回类型调用mapper.getFromNativeConverter(returnType)Function.java 对每个参数调用mapper.getToNativeConverter(type)若未配置TypeMapperJNA 走内置默认路径。TypeMapper接口定义了两个方向的方法见 TypeMapper.javapublic interface TypeMapper { FromNativeConverter getFromNativeConverter(Class? javaType); ToNativeConverter getToNativeConverter(Class? javaType); }默认实现DefaultTypeMapper维护toNativeConverters与fromNativeConverters两个注册表支持三种注册方式addToNativeConverter(cls, converter)注册 Java→Native 单向转换addFromNativeConverter(cls, converter)注册 Native→Java 单向转换addTypeConverter(cls, converter)注册双向转换。DefaultTypeMapper的getAltClass方法会自动将int.class与Integer.class、boolean.class与Boolean.class等原始类型/包装类型配对注册因此注册一次即可覆盖两种写法。查找时按注册顺序做isAssignableFrom匹配见 DefaultTypeMapper.java支持父类/接口级别的宽泛匹配。ToNativeConverter的nativeType()返回值必须是 JNA 支持的原生类型集合Pointer、Boolean、Byte、Short、Character、Integer、NativeLong、Long、Float、Double、Structure、String、WString以及Buffer/原始类型数组注意后两者不支持 Direct 映射模式见 ToNativeConverter.java。五、自定义映射的三种方式5.1 通过 TypeMapper 定制接口级映射TypeMapper 通常用于解决“整个接口统一类型转换”的需求最典型的就是Javaboolean↔ Win32BOOL32 位整数。使用方式是把映射器实例作为TYPE_MAPPER选项传给Native.loadMapString, Object options new HashMap(); options.put(Library.OPTION_TYPE_MAPPER, new W32APITypeMapper()); MyLibrary lib Native.load(mylib, MyLibrary.class, options);相关选项键定义在 Library.javaOPTION_TYPE_MAPPER type-mapper。Native.load会在加载库时读取该选项并传递给后续的函数调用见 Native.java。DefaultTypeMapper适合作为自定义映射器的基类在构造器中追加自己的转换规则。仓库中的 TypeMapperTest.java 提供了可直接运行的完整示例例如把Boolean映射成魔法值整数见 TypeMapperTest.javaDefaultTypeMapper mapper new DefaultTypeMapper(); mapper.addToNativeConverter(Boolean.class, new ToNativeConverter() { Override public Object toNative(Object arg, ToNativeContext ctx) { return Integer.valueOf(Boolean.TRUE.equals(arg) ? MAGIC : 0); } Override public Class? nativeType() { return Integer.class; } }); TestLibrary lib Native.load(testlib, TestLibrary.class, Collections.singletonMap(Library.OPTION_TYPE_MAPPER, mapper)); assertEquals(MAGIC, lib.returnInt32Argument(true));测试中还演示了双向转换Integer → Boolean见 TypeMapperTest.java、String ↔ WString转换TypeMapperTest.java以及Structure 字段级别的映射把结构体中的boolean字段映射为 4 字节整数后Structure.size()变为 4、写入内存的值变为 1/0TypeMapperTest.java。5.2 通过 NativeMapped 实现类级映射如果只有某一类特定对象需要特殊映射可以让该类型实现NativeMapped接口。它要求实现三个方法public interface NativeMapped { Object fromNative(Object nativeValue, FromNativeContext context); Object toNative(); Class? nativeType(); }NativeLong、IntegerType的子类正是走这条路。实现类必须提供无参构造器NativeMappedConverter通过反射实例化。典型的枚举映射写法public enum Status implements NativeMapped { OK(0), ERROR(-1); private final int code; Status(int code) { this.code code; } Override public Object toNative() { return code; } Override public Object fromNative(Object v, FromNativeContext c) { return valueOf(((Number) v).intValue()); } Override public Class? nativeType() { return Integer.class; } }这样接口方法签名中直接使用Status即可JNA 会经NativeMappedConverter自动完成双向转换枚举的完整用法同样可见于 TypeMapperTest.java。5.3 通过 FunctionMapper 定制函数名映射除了类型映射JNA 还允许定制Java 方法名 → 原生函数名的映射对应选项键OPTION_FUNCTION_MAPPER function-mapper见 Library.java。典型实现是 Windows 平台的StdCallFunctionMapperstdcall 约定要求导出函数名带有字节数后缀该实现会计算每个参数的原生栈尺寸并拼出形如FuncName12的装饰名找不到时再尝试带下划线的_FuncName12最后回退到未装饰名见 StdCallFunctionMapper.java。MapString, Object options new HashMap(); options.put(Library.OPTION_FUNCTION_MAPPER, new StdCallFunctionMapper()); MyWin32Lib lib Native.load(user32, MyWin32Lib.class, options);需要留意若同时使用了自定义类型映射getArgumentNativeStackSize会根据NativeMapped的实际原生类型推算栈大小对于原生类型尺寸未知的自定义类可通过覆写该方法补充见 StdCallFunctionMapper.java。六、实践建议与常见误区long别用 Javalong直连除非你明确目标平台是 64 位 Unix此时long恰好 64 位跨平台代码应统一使用NativeLong否则 Windows 上会栈错位boolean默认是 4 字节整数getNativeSize中boolean/Boolean硬编码为 4C 头文件里的BOOL就按此处理若是 C99 的_Bool1 字节需要自定义映射char不等同于 1 字节JNA 的char按wchar_t尺寸2 或 4 字节处理映射 C 的 8 位char应使用byte映射器顺序敏感DefaultTypeMapper按注册顺序查找先注册的转换器优先命中注册宽泛类型如CharSequence时要放在具体类型之后无符号类型用同尺寸有符号类型承接仅当需要访问无符号位模式时才考虑IntegerType(..., true)等带unsigned标志的包装。七、进一步阅读默认映射总表原文档www/Mappings.md自定义映射官方说明www/CustomMappings.md映射器接口与默认实现TypeMapper.java、DefaultTypeMapper.java、NativeMapped.java平台尺寸探测与原生尺寸表Native.java可运行测试样例TypeMapperTest.javastdcall 函数名映射器StdCallFunctionMapper.java赞分享系统编程后端【免费下载链接】jnaJava Native Access项目地址https://gitcode.com/gh_mirrors/jn/jna点击查看免费下载相关推荐BLOOM-3B模型深度解析30亿参数如何实现45种自然语言与12种编程语言支持 BLOOM 3B模型深度解析30亿参数如何实现45种自然语言与12种编程语言支持 想要了解如何用30亿参数的AI模型支持45种自然语言和12种编程语言吗PyO3 类型转换对照表全解析Rust 类型与 Python 类型的一一映射PyO3 类型转换对照表全解析Rust 类型与 Python 类型的一一映射 导读 在 PyO3 中编写可被 Python 调用的函数 pyfunctio开发工具TypeGraphQL类型转换自定义类型映射规则TypeGraphQL类型转换自定义类型映射规则 TypeGraphQL通过类型映射机制实现TypeScript类型与GraphQL标量 Scalar 的转换后端GraphQLAPI设计上一篇Docker垃圾清理终极指南12个环境变量全面掌握配置技巧下一篇ODS本地STT实战Whisper语音识别私有化部署完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表