john16384
john16384

Reputation: 8064

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?

Upvotes: 1

Views: 1202

Answers (2)

technomage
technomage

Reputation: 10069

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.

Upvotes: 1

john16384
john16384

Reputation: 8064

Looks like I found the issue, although I'm not 100% sure what is happening.

It seems that using W32APIOptions.DEFAULT_OPTIONS means it will use the UNICODE settings (because w32.ascii property is false). This looked okay to me, as mpv-1.dll works with UTF-8 strings only, which is Unicode.

However, now I'm guessing that in this case it means it will call a wide-char version of the library function (and if that doesn't exist, still call the original function), and probably means it encodes Strings with two bytes per character. This is because most Win32 libraries have an ASCII and WIDE version of methods accepting strings, but nothing for UTF-8.

Since mpv-1.dll only accepts UTF-8 (and isn't really Win32), strings should be just encoded as bytes in UTF-8 format (basically, just leave them alone). To let JNA know this, either donot pass a W32APIOptions map at all, or select the ASCII_OPTIONS manually.

Upvotes: 1

Related Questions