Using a ByteBuffer to represent a string in a JNA call results in extra characters in the buffer

1.1k views Asked by At

I'm calling a dll using JNA and code generated using Jnaerator. One of the methods requires an string, and the JNA signature takes a ByteBuffer.

I've tried allocating the ByteBuffer as direct (ByteBuffer.allocateDirect) and indirect (ByteBuffer.wrap) but in both cases some times the string that reaches the dll has additional random characters (e.g. ReceiptÚeœ ). The original byte[] is there (receipt = 52 65 63 65 69 70 74) but as well a variable number of additional random bytes (01 da 65 9c 19). Randomly the string is correct, with no additional bytes.

I've tried the equivalent code using BridJ instead of JNA (the method signature takes then a Pointer name) and it in that case it works fine. Unfortunately I can't switch to BridJ because I need to use the com.sun.jna.platform.win32 classes, unless I can generate a BridJ replacement for those (https://stackoverflow.com/questions/31658862/jnaerator-bridj-user32-missing-methods)

Native declaration:

HRESULT extern WINAPI WFSOpen ( LPSTR lpszLogicalName, HAPP hApp, LPSTR lpszAppID,DWORD dwTraceLevel, DWORD dwTimeOut, DWORD dwSrvcVersionsRequired, LPWFSVERSION lpSrvcVersion, LPWFSVERSION lpSPIVersion, LPHSERVICE lphService);

JNAerator JNA code:

//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 call working (but deprecated)

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 call NOT working test A:

lpszLogicalName = ByteBuffer.wrap(bytes);
 MsxfsLibrary.INSTANCE.WFSOpen(lpszLogicalName, lphApp.getValue(), lpszAppID, dwTraceLevel, dwTimeOut, dwSrvcVersionsRequired, lpSrvcVersion, lpSPIVersion, lphService);

Java call NOT working test 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);
2

There are 2 answers

1
technomage On

If you're referring to _wfsopen(), it's expecting a wide-character string. Either use WString, or configure your library to type-map String (see W32APIOptions.UNICODE_OPTIONS).

0
ahbejarano On

I think what's happening is that you are passing an array of bytes that contains your string but it is not null terminated string. You should create a byte array with an extra position. Set that las position to 0 and copy your string into that byte array.