我使用JNA从java调用c函数。函数将字符串列表写入用户提供的内存,其签名读取:
void c_getStrings(char *buf, size_t bufSize, char *strings[], size_t *stringsCount)
对于Java版本:
public interface TestCaseDLL extends Library
{
int c_getStrings(byte[] buf, int bufSize, Memory strings, IntByReference stringCount);
}
public class TestCase
{
public static void main(String[] args)
{
byte[] buf = new byte[100];
Memory strings = new Memory(Memory.SIZE * 10);
IntByReference stringCount = new IntByReference(10);
// c_getStrings() will write the strings continuously to 'buf' and
// additionally return a list of starting addresses through the
// 'strings' parameter (that is 'strings' point into 'buf').
// 'stringCount' holds the initial array size of 'strings' and will
// return the count of returned strings.
TestCaseDLL.INSTANCE.c_getStrings(buf, buf.length, strings, stringCount);
System.out.println(strings.getPointer(0).getString(0));
System.out.printf("%c\n", buf[0]); // how can this line change 'strings'?
System.out.println(strings.getPointer(0).getString(0));
for (byte b: buf) {
System.out.print((char) b);
}
System.out.println("");
for (byte b: buf) {
System.out.printf("%#x ", b);
}
System.out.println("");
}
}
输出
??llo world!
H
?
Hello world! Hallo Welt! Ciao a tutti!
0x48 0x65 0x6c 0x6c 0x6f 0x20 0x77 0x6f 0x72 0x6c 0x64 0x21 0x0 0x48 0x61 0x6c 0x6c 0x6f 0x20 0x57 0x65 0x6c 0x74 0x21 0x0 0x43 0x69 0x61 0x6f 0x20 0x61 0x20 0x74 0x75 0x74 0x74 0x69 0x21 0x0 ...
我遇到了以下问题:
返回的字符串已断开。它应该返回“Hello World!”而不是“?“世界啊!”
打印将更改返回的字符串。我现在知道这里发生了什么,因为我只是在读它的价值。
我的类型映射是坏了还是缺少了一些基本的东西?
更新
我现在使用
void c_getStrings(Memory buf, int bufSize, String[] strings, IntByReference stringCount);
如果我要重做,我会按照technomage的建议将它分成两个函数:
void c_fill(char *buf, size_t bufSize);
void c_parseToStringArray(const char *buf, const char *strings[], size_t stringsSize);
最佳答案
前几个技术要点:
由于第三个参数引用了第一个参数,因此不能使用基元数组。因为它显式地保存了本机指针,所以使用NIO缓冲区会很尴尬。这就留给你Memory
。但是,如果只需要得到的String
值而不关心指针本身,那么只要第三个参数是byte[]
类型,NIO缓冲区或Memory
就可以工作。
第三个参数似乎是一个可写的指针数组。您可以使用String[]
或Pointer[]
。您也可以使用String[]
,前提是它的分配足够大,可以容纳所写的尽可能多的指针值。
然后是更大的问题:
为什么您需要一个缓冲区(可能有嵌入的NUL字符)和一个单独的指针到这个缓冲区中?
当从JNA映射时,这看起来很傻,因为JNA自己做字符串分配。如果只是将内存缓冲区作为池字符串存储提供,那么从Memory
第三个参数获取字符串后就不需要它了。如果您打算操作缓冲区中的内容,则此类更改不会反映在“返回”字符串中。