Search code examples
javamemory-managementjna

JNA passing Reference to Byte Array does not work correctly


my native code is:

int xlSetApplConfig(
char *appName,
unsigned int appChannel,
unsigned int hwType,
unsigned int hwIndex,
unsigned int hwChannel,
unsigned int busType)

I tried to express appName with a Pointer in Java like this:

    //integers..
    byte[] appnamebyte = new String("JEdit").getBytes();
    Pointer appname = new Memory(appnamebyte.length);
    appname.write(0, appnamebyte, 0, appnamebyte.length);
    int status = dll.INSTANCE.xlOpenPort(portHandle, appname, channelMask, permissionMask, 
    rxQueueSize,
    xlInterfaceVersion, busType);

the function is writing the byte array to the registry or somewhere and with a other Tool(given one) I can read the value. If I start my code there is no error but the appname is not allways correct. Sometimes there are more charcacters than i want("JEdit ?78ê"). Maybe somehow more memory than I want is allocated. I also tried to pass an normal byte[] but with the same problem


Solution

  • The problem you're encountering is that C strings must be null terminated. Simply grabbing the bytes from the String only gives you the characters J, E, d, i, and t. Your array needs to have a sixth byte with a value of 0 to work properly.

    One way to do this is to keep the Pointer mapping you have and allocate the extra byte yourself and then write the bytes to that native memory using Pointer.setString():

    Memory buffer = new Memory(6);
    // make sure the last byte is 0
    memory.clear(); // could also just set the last byte
    buffer.setString(0, "JEdit");
    

    (Note that this relies on the default platform encoding, which is generally a bad idea. You should specify the encoding; in this case US-ASCII should work.

    Instead of setString() you could also keep write() as you do, with the 0-terminated byte[] argument.

    Alternately if you want to use an array mapping in the function instead of a Pointer, just declare a new array:

    // initializes all elements to 0
    byte[] appnameNullTerminated = new byte[appname.length+1];
    System.arraycopy(appname, 0, appnameNullTerminated, 0, appname.length);
    

    Or, use the above and write that terminated byte[] array as you do now.

    Again, make sure you control the encoding of the characters to bytes. Using getBytes() without specifying an encoding is generally a bad idea.