使用 String 的 JNA 调用与使用 byte[] 的行为不同
JNA call with String behaves differently from one with byte[]
我有一个 JNA Java 接口用于 C 函数 mpv_set_option_string
定义为:
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[=13=]".getBytes(StandardCharsets.UTF_8),
"always[=13=]".getBytes(StandardCharsets.UTF_8)
);
...它 returns 没有错误(0
)并且工作正常(或者看起来如此)。
我不明白的是,JNA 应该默认使用 UTF-8 编码将 String
编码为 char *
并且 NUL
终止(正是我手动执行的操作) ,但我得到了不同的结果。
任何人都可以阐明这一点吗?
看起来我发现了问题,虽然我不是 100% 确定发生了什么。
似乎使用W32APIOptions.DEFAULT_OPTIONS
意味着它将使用UNICODE 设置(因为w32.ascii
属性 是false
)。这对我来说没问题,因为 mpv-1.dll
仅适用于 UTF-8 字符串,即 Unicode。
但是,现在我猜测,在这种情况下,这意味着它将调用库函数的 wide-char 版本(如果不存在的话,仍然调用原始函数),并且可能意味着它使用每个字符两个字节对字符串进行编码。这是因为大多数 Win32 库都有接受字符串的 ASCII 和 WIDE 版本的方法,但没有接受 UTF-8 的方法。
因为 mpv-1.dll
只接受 UTF-8(并不是真正的 Win32),字符串应该只编码为 UTF-8 格式的字节(基本上,不要管它们)。要让 JNA 知道这一点,要么根本不传递 W32APIOptions
映射,要么手动 select ASCII_OPTIONS
。
您不应该将 W32OPTIONS 传递给非 WIN32 的库 API。
默认情况下,JNA 将 String
映射到 char*
,因此删除选项应该可以解决您的问题。
您还应该为句柄使用显式本机类型,而不是 Java long
。 Pointer
在这种情况下可能是正确的。
我有一个 JNA Java 接口用于 C 函数 mpv_set_option_string
定义为:
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[=13=]".getBytes(StandardCharsets.UTF_8),
"always[=13=]".getBytes(StandardCharsets.UTF_8)
);
...它 returns 没有错误(0
)并且工作正常(或者看起来如此)。
我不明白的是,JNA 应该默认使用 UTF-8 编码将 String
编码为 char *
并且 NUL
终止(正是我手动执行的操作) ,但我得到了不同的结果。
任何人都可以阐明这一点吗?
看起来我发现了问题,虽然我不是 100% 确定发生了什么。
似乎使用W32APIOptions.DEFAULT_OPTIONS
意味着它将使用UNICODE 设置(因为w32.ascii
属性 是false
)。这对我来说没问题,因为 mpv-1.dll
仅适用于 UTF-8 字符串,即 Unicode。
但是,现在我猜测,在这种情况下,这意味着它将调用库函数的 wide-char 版本(如果不存在的话,仍然调用原始函数),并且可能意味着它使用每个字符两个字节对字符串进行编码。这是因为大多数 Win32 库都有接受字符串的 ASCII 和 WIDE 版本的方法,但没有接受 UTF-8 的方法。
因为 mpv-1.dll
只接受 UTF-8(并不是真正的 Win32),字符串应该只编码为 UTF-8 格式的字节(基本上,不要管它们)。要让 JNA 知道这一点,要么根本不传递 W32APIOptions
映射,要么手动 select ASCII_OPTIONS
。
您不应该将 W32OPTIONS 传递给非 WIN32 的库 API。
默认情况下,JNA 将 String
映射到 char*
,因此删除选项应该可以解决您的问题。
您还应该为句柄使用显式本机类型,而不是 Java long
。 Pointer
在这种情况下可能是正确的。