2012-12-27 42 views
5

Tôi đang làm việc trên các chức năng gọi từ tệp Delphi được biên dịch * .so từ một chương trình Java. Sau khi một số nghiên cứu nó có vẻ như JNA là ông cách để đi. Trước khi đi sâu vào một số mã Delphi phức tạp, tôi đang cố gắng chơi với một số mã "Hello World" nhưng gặp sự cố khi nhận được một chuỗi được trả về bởi hàm Delphi.Làm thế nào tôi có thể gọi một hàm Delphi trả về một chuỗi bằng cách sử dụng JNA?

Mã Delphi (helloworld.pp):

library HelloWorldLib; 

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

exports HelloWorld; 

begin 
end. 

tôi biên dịch nó từ dòng lệnh với "FPC -Mdelphi helloworld.pp", trong đó sản xuất libhelloworld.so.

Bây giờ lớp Java của tôi:

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

Tuy nhiên khi tôi chạy mã Java này tôi nhận được:

# 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 

Lưu ý rằng nếu tôi thay đổi phương pháp Delphi của tôi (và giao diện Java liên quan) để trở về một số nguyên hardcoded, tất cả mọi thứ hoạt động tuyệt vời: chuỗi tôi vượt qua được in và tôi nhận được int trở lại như mong đợi.

Thật kỳ lạ, nếu phương thức Delphi trả về một char, tôi phải viết proxy JNA của tôi khi trả về một byte và chuyển nó thành char thủ công (nếu tôi khai báo giao diện của mình khi trả về char nó sẽ in ra một ký tự rác).

Có ý tưởng gì xảy ra ở đây không?

FYI, tôi đang sử dụng Ubuntu 12.04, 64 bit, sử dụng Sun JDK 1.7.0_10-b18, JNA 3.5.1 và Phiên bản trình biên dịch Pascal miễn phí 2.4.4-3.1.

+0

Bạn có thể trả về 'Chuỗi' khác từ hàm gốc hơn hàm đã được chuyển vào không? – JimmyB

+0

@HannoBinder: Tôi nhận được cùng một lỗi nếu tôi thay đổi mã Delphi của tôi để làm "Kết quả: = 'HELLO';". :( –

Trả lời

8

Một Delphi hoặc FreePascal string là loại được quản lý không thể được sử dụng làm loại JNA. JNA documentation giải thích rằng Java String được ánh xạ tới một con trỏ tới một mảng được kết thúc bằng null gồm 8 ký tự. Trong điều khoản Delphi là PAnsiChar.

Vì vậy, bạn có thể thay đổi thông số đầu vào trong mã Pascal của mình từ string thành PAnsiChar.

Giá trị trả lại có vấn đề hơn. Bạn sẽ cần phải quyết định ai phân bổ bộ nhớ. Và bất cứ ai phân bổ nó cũng phải giải phóng nó.

Nếu mã gốc chịu trách nhiệm phân bổ nó thì bạn cần phải phân bổ chuỗi phân bổ null. Và trả về một con trỏ đến nó. Bạn cũng cần phải xuất một bộ deallocator để mã Java có thể yêu cầu mã nguồn gốc để xử lý khối khối được cấp phát bộ nhớ.

Thường thuận tiện hơn khi phân bổ bộ đệm trong mã Java. Sau đó chuyển mã đó vào mã gốc và để nó điền nội dung của bộ đệm. câu hỏi Stack Overflow này minh họa kỹ thuật, sử dụng chức năng Windows API GetWindowText như ví dụ của nó: How can I read the window title with JNI or JNA?

Một ví dụ của việc này bằng Pascal sẽ là như vậy:

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; 

Về phía Java, tôi đoán mã sẽ giống như thế này, nhớ rằng tôi hoàn toàn không biết gì về 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); 
+0

Ahhh bạn nói đúng. Điều đó mang lại những ký ức về con trỏ, malloc, v.v ... đã biến mất sau nhiều năm làm việc với bộ thu gom rác JVM ... BTW có phương thức Delphi trả về một PAnsiChar làm việc với ánh xạ JNA . một string như kiểu trả về (nhưng sau đó mở ra vấn đề phân bổ bộ nhớ) –

+0

các lớp Java trông như thế này: public class HelloWorld { \t public interface HelloWorldLibrary mở rộng thư viện { \t \t HelloWorldLibrary INSTANCE = (HelloWorldLibrary) Native.loadLibrary ("/home/clevesque/Desktop/delphi/libhelloworld.so", HelloWorldLibrary.class); \t \t int GetText (byte [] đệm); \t} \t chính trống tĩnh chính (Chuỗi [] args) { \t \t byte [] buffer = new byte [512]; \t \t System.out.println (HelloWorldLibrary.INSTANCE.GetText (buffer)); \t \t System.out.println (Native.toString (buffer)); \t} } –

+0

Thật vậy, trả về 'PAnsiChar' là tốt modulo deallocating bộ nhớ. –

0

Bên cạnh đó, việc sử dụng stdcall trên Linux 64 bit cũng không hoàn toàn hợp lý. Nó có thể hoạt động, vì thường chỉ có một quy ước gọi trên một mục tiêu 64-bit, nhưng chính xác, nó không phải là. Sử dụng cdecl;

+0

Chắc chắn gọi trang trí quy ước chỉ là bỏ qua cho các mục tiêu x64. Nếu cùng một mã được biên dịch cho x86 thì quy ước gọi trở nên quan trọng trở lại. –

+0

Vâng, đó là chính xác những gì tôi đang nói phải không? Xảy ra để được làm việc là khác nhau từ chính xác. –

+0

Người ta tự hỏi StdCallLibrary nghĩa là gì trên Linux –

Các vấn đề liên quan