在 JNA 调用中使用 ByteBuffer 表示字符串会导致缓冲区中出现额外字符
Posted
技术标签:
【中文标题】在 JNA 调用中使用 ByteBuffer 表示字符串会导致缓冲区中出现额外字符【英文标题】:Using a ByteBuffer to represent a string in a JNA call results in extra characters in the buffer 【发布时间】:2015-07-28 08:32:36 【问题描述】:我正在使用 JNA 调用 dll,并使用 Jnaerator 生成代码。其中一种方法需要一个字符串,而 JNA 签名需要一个 ByteBuffer。
我尝试将 ByteBuffer 分配为直接 (ByteBuffer.allocateDirect) 和间接 (ByteBuffer.wrap) 但在这两种情况下 有时 到达 dll 的字符串都有额外的随机字符(例如 ReceiptÚeœ )。原始字节 [] 在那里(收据 = 52 65 63 65 69 70 74),但还有可变数量的附加随机字节(01 da 65 9c 19)。随机字符串是正确的,没有额外的字节。
我尝试了使用 BridJ 而不是 JNA 的等效代码(方法签名采用指针名称),在这种情况下它可以正常工作。不幸的是,我无法切换到 BridJ,因为我需要使用 com.sun.jna.platform.win32 类,除非我可以为这些类生成 BridJ 替代品 (https://***.com/questions/31658862/jnaerator-bridj-user32-missing-methods)
本土宣言:
HRESULT extern WINAPI WFSOpen ( LPSTR lpszLogicalName, HAPP hApp, LPSTR lpszAppID,DWORD dwTraceLevel, DWORD dwTimeOut, DWORD dwSrvcVersionsRequired, LPWFSVERSION lpSrvcVersion, LPWFSVERSION lpSPIVersion, LPHSERVICE lphService);
JNAerator JNA 代码:
//works
@Deprecated
NativeLong WFSOpen(Pointer lpszLogicalName, Pointer hApp, Pointer lpszAppID, int dwTraceLevel, int dwTimeOut, int dwSrvcVersionsRequired, WFSVERSION lpSrvcVersion, WFSVERSION lpSPIVersion, ShortByReference lphService);
//does not work
NativeLong WFSOpen(ByteBuffer lpszLogicalName, Pointer hApp, ByteBuffer lpszAppID, int dwTraceLevel, int dwTimeOut, int dwSrvcVersionsRequired, WFSVERSION lpSrvcVersion, WFSVERSION lpSPIVersion, ShortBuffer lphService);
Java 调用有效(但已弃用)
Pointer m = new Memory(string.length() + 1); // WARNING: assumes ascii-only string
m.setString(0, string);
MsxfsLibrary.INSTANCE.WFSOpen(lpszLogicalName, lphApp.getValue(), lpszAppID, dwTraceLevel, dwTimeOut, dwSrvcVersionsRequired, lpSrvcVersion, lpSPIVersion, lphService);
Java 调用不工作测试 A:
lpszLogicalName = ByteBuffer.wrap(bytes);
MsxfsLibrary.INSTANCE.WFSOpen(lpszLogicalName, lphApp.getValue(), lpszAppID, dwTraceLevel, dwTimeOut, dwSrvcVersionsRequired, lpSrvcVersion, lpSPIVersion, lphService);
Java 调用不工作测试 B:
byte[] bytes = string.getBytes();
return ByteBuffer.wrap(bytes);
ByteBuffer bb = ByteBuffer.allocateDirect(bytes.length);
bb.put(bytes);
lpszLogicalName = bb.position(0);
msxfsLibrary.WFSOpen(lpszLogicalName, lphApp.getValue(), lpszAppID, dwTraceLevel, dwTimeOut, dwSrvcVersionsRequired, lpSrvcVersion, lpSPIVersion, lphService);
【问题讨论】:
您的本机代码期望什么?你的原生声明是什么?你的Java映射是什么?您的本机代码是否需要const char *
或 const wchar_t *
?通常 JNA 将 String
映射到前者,但这种行为可能会有所不同,具体取决于您初始化库映射的方式。如果这是本机代码所期望的,为什么不直接将签名更改为 String
?
本机声明是:HRESULT extern WINAPI WFSOpen ( LPSTR lpszLogicalName
Jnaerator JNA 随机失败,转换为:NativeLong WFSOpen(ByteBuffer lpszLogicalName,...
Jnaerator Bridj 工作正常,转换为:public static long WFSOpen(Pointer<Byte > lpszLogicalName
更新您的问题并删除这些 cmets。
请更新您的问题以包含本机和 Java 声明,以及您的实际使用情况。
请用更多信息更新您的QUESTION。在 cmets 中无法读取。
【参考方案1】:
如果您指的是_wfsopen(),它需要一个宽字符串。要么使用WString
,要么将你的库配置为类型映射String
(参见W32APIOptions.UNICODE_OPTIONS
)。
【讨论】:
不,这不是 Visual Studio wfsopen。它是 CEN-XFS 的,它将 LPSTR lpszLogicalName 作为第一个参数。【参考方案2】:我认为正在发生的事情是您正在传递一个包含您的字符串的字节数组,但它不是以空值结尾的字符串。您应该创建一个带有额外位置的字节数组。将该 las 位置设置为 0 并将您的字符串复制到该字节数组中。
【讨论】:
以上是关于在 JNA 调用中使用 ByteBuffer 表示字符串会导致缓冲区中出现额外字符的主要内容,如果未能解决你的问题,请参考以下文章
JNA/ByteBuffer 没有被释放并导致 C 堆内存不足