使用String的JNA调用的行为与使用byte []的行为不同

时间:2019-06-14 23:56:58

标签: java jna mpv

我有一个C函数mpv_set_option_string的JNA Java接口,定义为:

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);
}

当我这样称呼时:

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");

我从上次调用中返回错误(-5),表明找不到选项(keep-open)。

但是,如果我将JNA函数签名更改为:

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

...然后这样称呼它:

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

...它不返回错误(0)并且可以正常工作(或看起来如此)。

我没有得到的是,JNA应该默认将String编码为char *并使用UTF-8编码,并且终止NUL(正是我手动执行的操作),但是我得到不同的结果。

有人能对此有所启发吗?

2 个答案:

答案 0 :(得分:1)

虽然我不确定100%发生了什么,但看起来还是找到了问题。

似乎使用W32APIOptions.DEFAULT_OPTIONS意味着它将使用UNICODE设置(因为w32.ascii属性为false)。这对我来说还不错,因为mpv-1.dll仅适用于Unicode的UTF-8字符串。

但是,现在我猜到了,在这种情况下,这意味着它将调用库函数的宽字符版本(如果不存在,则仍调用原始函数) ,这可能意味着它以每个字符两个字节的形式编码字符串。这是因为大多数Win32库具有接受字符串的ASCII和WIDE版本的方法,而对于UTF-8则没有。

由于mpv-1.dll仅接受UTF-8(实际上不是Win32),因此字符串应仅以UTF-8格式编码为字节(基本上,不理会它们)。为了让JNA知道这一点,要么根本不传递W32APIOptions映射,要么手动选择ASCII_OPTIONS

答案 1 :(得分:1)

您不应将W32OPTIONS传递给非WIN32 API的库。

默认情况下,JNA将String映射到char*,因此删除选项应该可以为您解决问题。

对于句柄,还应该使用显式本机类型,而不要使用Java long。在这种情况下,Pointer可能是正确的。