如何调用使用JNA返回字符串的Delphi函数?

Chr*_*e L 6 java delphi string freepascal jna

我正在从Java程序中调用来自Delphi编译的*.so文件的函数.经过一些研究,似乎JNA是他的出路.在深入研究一些复杂的Delphi代码之前,我正在尝试使用一些"Hello World"代码,但是在获取Delphi函数返回的字符串时遇到了麻烦.

Delphi代码(helloworld.pp):

library HelloWorldLib;

function HelloWorld(const myString: string): string; stdcall;
begin
  WriteLn(myString);
  Result := myString;
end;

exports HelloWorld;

begin
end.
Run Code Online (Sandbox Code Playgroud)

我使用" fpc -Mdelphi helloworld.pp " 从命令行编译它,生成libhelloworld.so.

现在我的Java类:

import com.sun.jna.Library;
import com.sun.jna.Native;

public class HelloWorld {
    public interface HelloWorldLibrary extends Library {
        HelloWorldLibrary INSTANCE = (HelloWorldLibrary) Native.loadLibrary("/full/path/to/libhelloworld.so", HelloWorldLibrary.class);

        String HelloWorld(String test);
    }

    public static void main(String[] args) {
        System.out.println(HelloWorldLibrary.INSTANCE.HelloWorld("QWERTYUIOP"));
    }
}
Run Code Online (Sandbox Code Playgroud)

但是当我运行这个Java代码时,我得到:

# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007f810318add2, pid=4088, tid=140192489072384
#
# JRE version: 7.0_10-b18
# Java VM: Java HotSpot(TM) 64-Bit Server VM (23.6-b04 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# C  [libhelloworld.so+0xbdd2]  HelloWorld+0x6fea
Run Code Online (Sandbox Code Playgroud)

请注意,如果我更改我的Delphi方法(和相关的Java接口)以返回硬编码整数,一切都很好:我传递的字符串被打印,我按预期返回int.

奇怪的是,如果Delphi方法返回一个char,我必须编写我的JNA代理作为返回一个字节并手动将其转换为char(如果我声明我的接口返回一个char它打印出一个垃圾字符).

知道这里出了什么问题吗?

仅供参考,我在Ubuntu 12.04,64bits,使用Sun JDK 1.7.0_10-b18,JNA 3.5.1和Free Pascal Compiler版本2.4.4-3.1.

Dav*_*nan 8

Delphi或FreePascal string是一种托管类型,不能用作JNA类型.所述JNA文档解释的Java String被映射到的指针的8个字符的空终止阵列.用Delphi术语表示PAnsiChar.

因此,您可以将Pascal代码中的输入参数更改stringPAnsiChar.

返回值更成问题.您需要决定谁分配内存.分配它的人也必须释放它.

如果本机代码负责分配它,那么您需要堆分配以null结尾的字符串.并返回指向它的指针.您还需要导出解除分配器,以便Java代码可以要求本机代码释放堆分配的内存块.

在Java代码中分配缓冲区通常更方便.然后将其传递给本机代码,让它填写缓冲区的内容.此Stack Overflow问题说明了该技术,使用Windows API函数GetWindowText作为示例:如何使用JNI或JNA读取窗口标题?

使用Pascal的一个例子是这样的:

function GetText(Text: PAnsiChar; Len: Integer): Integer; stdcall;
const
  S: AnsiString = 'Some text value';
begin
  Result := Length(S)+1;//include null-terminator
  if Len>0 then
    StrPLCopy(Text, S, Len-1);
end;
Run Code Online (Sandbox Code Playgroud)

在Java方面,我想代码看起来像这样,记住我对Java一无所知.

public interface MyLib extends StdCallLibrary {
    MyLib INSTANCE = (MyLib) Native.loadLibrary("MyLib", MyLib.class);
    int GetText(byte[] lpText, int len);
}

....

int len = User32.INSTANCE.GetText(null);
byte[] arr = new byte[len];
User32.INSTANCE.GetText(arr, len);
String Text = Native.toString(arr);
Run Code Online (Sandbox Code Playgroud)