6

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.

libhelloworld.soを生成する" fpc -Mdelphi helloworld.pp "を使用して、コマンド ラインからコンパイルします。

今私の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"));
    }
}

ただし、この 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

ハードコードされた整数を返すように Delphi メソッド (および関連する Java インターフェイス) を変更すると、すべてがうまく機能することに注意してください。渡す文字列が出力され、期待どおりに int が返されます。

奇妙なことに、Delphi メソッドが char を返す場合、バイトを返すように JNA プロキシを記述し、それを手動で char にキャストする必要があります (インターフェイスが char を返すと宣言すると、文字化けが出力されます)。

ここで何がうまくいかないのですか?

参考までに、Sun JDK 1.7.0_10-b18、JNA 3.5.1、および Free Pascal Compiler バージョン 2.4.4-3.1 を使用して、64 ビットの Ubuntu 12.04 を使用しています。

4

2 に答える 2

8

Delphi または FreePascalstringは、JNA 型として使用できないマネージド型です。JNAのドキュメントでは、JavaStringが 8 ビット文字のヌル終了配列へのポインターにマップされていると説明しています。Delphi 用語では、PAnsiChar.

stringしたがって、Pascal コードの入力パラメーターを からに変更できますPAnsiChar

戻り値はもっと問題です。誰がメモリを割り当てるかを決める必要があります。そして、それを割り当てた人は誰でもそれを解放しなければなりません。

ネイティブコードが割り当てを担当している場合は、null で終わる文字列をヒープに割り当てる必要があります。そして、それへのポインタを返します。また、Java コードがネイティブ コードに、ヒープに割り当てられたメモリ ブロックの割り当てを解除するように要求できるように、デアロケーターをエクスポートする必要があります。

通常は、Java コードでバッファを割り当てる方が便利です。次に、それをネイティブ コードに渡し、バッファの内容を埋めます。このスタック オーバーフローの質問は、Windows API 関数GetWindowTextを例として使用して、この手法を示しています: How can I read the window title with JNI or 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;

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);
于 2012-12-27T15:57:17.557 に答える
0

さらに、64ビットLinuxでstdcallを使用することも完全に論理的ではありません。通常、64ビットターゲットには呼び出し規約が1つしかないため、おそらく機能しますが、正しくありません。cdeclを使用します。

于 2012-12-28T22:58:21.440 に答える