Search code examples
javajnampv

JNA call with String behaves differently from one with byte[]


I have a JNA Java interface for a C function mpv_set_option_string defined as:

public interface MPV extends StdCallLibrary {
    MPV INSTANCE = Native.loadLibrary("lib/mpv-1.dll", MPV.class, W32APIOptions.DEFAULT_OPTIONS);

    long mpv_create();
    int mpv_initialize(long handle);
    int mpv_set_option_string(long handle, String name, String data);
}

When I call this like this:

System.setProperty("jna.encoding", "UTF8");

long handle = MPV.INSTANCE.mpv_create();
int error = MPV.INSTANCE.mpv_initialize(handle);
error = MPV.INSTANCE.mpv_set_option_string(handle, "keep-open", "always");

I get an error back (-5) from the last call, indicating the option (keep-open) is not found.

However, if I change the JNA function signature to:

int mpv_set_option_string(long handle, byte[] name, byte[] data);

...and then call it like this:

error = MPV.INSTANCE.mpv_set_option_string(
    handle, 
    "keep-open\0".getBytes(StandardCharsets.UTF_8),
    "always\0".getBytes(StandardCharsets.UTF_8)
);

...it returns no error (0) and works correctly (or so it seems).

What I don't get is, JNA is supposed to encode String by default as char * with UTF-8 encoding and NUL terminated (exactly what I do manually), yet I get different results.

Anyone able to shed some light on this?


Solution

  • You shouldn't be passing W32OPTIONS to a library that isn't a WIN32 API.

    By default, JNA maps String to char*, so removing the options should fix the issue for you.

    You should also be using an explicit native type for your handle instead of Java long. Pointer is probably correct in this case.