2012-12-27 16 views
5

Sto lavorando per chiamare le funzioni da un file * .so compilato da Delphi da un programma Java. Dopo alcune ricerche sembra che JNA sia la strada da percorrere. Prima di immergermi in un codice Delphi complesso, sto provando a giocare con il codice "Hello World", ma sto avendo problemi nel trovare una stringa restituita da una funzione Delphi.Come posso chiamare una funzione Delphi che restituisce una stringa usando JNA?

Il codice Delphi (helloworld.pp):

library HelloWorldLib; 

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

exports HelloWorld; 

begin 
end. 

compilo dalla riga di comando con "FPC helloworld.pp -Mdelphi", che produce libhelloworld.so.

Ora la mia classe 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")); 
    } 
} 

Tuttavia quando si esegue questo codice Java ottengo:

# 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 

Nota che se cambio il mio metodo Delphi (e l'interfaccia Java associata) per tornare un intero hardcoded, tutto funziona alla grande: la stringa che passo viene stampata e ottengo l'int back come previsto.

Stranamente, se il metodo Delphi restituisce un carattere, devo scrivere il mio proxy JNA come restituendo un byte e trasferirlo manualmente in char (se dichiaro che la mia interfaccia restituisce un carattere, stampa un carattere inutile).

Qualche idea di cosa sta andando storto qui?

FYI, Sono su Ubuntu 12.04, 64 bit, utilizzando Sun JDK 1.7.0_10-b18, JNA 3.5.1 e Free Pascal Compiler versione 2.4.4-3.1.

+0

È possibile restituire altri 'String's dalla funzione nativa rispetto a quella passata? – JimmyB

+0

@HannoBinder: Ottengo lo stesso errore se cambio il mio codice Delphi per fare "Risultato: = 'HELLO';". :( –

risposta

8

Un Delphi o FreePascal string è un tipo gestito che non può essere utilizzato come tipo JNA. JNA documentation spiega che Java String è mappato a un puntatore a un array con terminazione null di caratteri a 8 bit. In termini di Delphi è PAnsiChar.

Quindi è possibile modificare il parametro di input nel codice Pascal da string a PAnsiChar.

Il valore restituito è più problematico. Dovrai decidere chi assegna la memoria. E chi lo assegna deve anche liberarlo.

Se il codice nativo è responsabile dell'allocazione, è necessario eseguire l'allocazione della stringa con terminazione null. E restituire un puntatore ad esso. Dovresti anche esportare un deallocator in modo che il codice Java possa chiedere al codice nativo di deallocare il blocco di memoria assegnato all'heap.

Di solito è più conveniente allocare un buffer nel codice Java. Quindi passa al codice nativo e lascia che compili il contenuto del buffer. Questa domanda Stack Overflow illustra la tecnica, usando la funzione API di Windows GetWindowText come il suo esempio: How can I read the window title with JNI or JNA?

Un esempio di questo usando Pascal sarebbe in questo modo:

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; 

Sul lato Java, immagino il codice sarebbe simile a questo, tenendo presente che non conosco assolutamente nulla di 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 hai ragione.Questo riporta ricordi di puntatori, malloc, ecc. Che erano scomparsi in anni di lavoro con il garbage collector JVM ... BTW con il metodo Delphi restituisce un PAnsiChar funziona con una mappatura JNA di . una stringa come tipo di ritorno (ma poi si apre la questione di allocazione di memoria) –

+0

la classe Java assomiglia a questo: public class HelloWorld { \t interfaccia pubblica HelloWorldLibrary estende Biblioteca { \t \t HelloWorldLibrary GRADO = (HelloWorldLibrary) Native.loadLibrary ("/home/clevesque/Desktop/delphi/libhelloworld.so", HelloWorldLibrary.class); \t \t int GetText (byte [] buffer); \t} \t void main (String [] args) {public static \t \t byte [] tampone = new byte [512]; \t \t System.out.println (HelloWorldLibrary.INSTANCE.GetText (buffer)); \t \t System.out.println (Native.toString (buffer)); \t} } –

+0

Infatti, restituire 'PAnsiChar' è corretto modulo deallocare la memoria. –

0

Inoltre, l'utilizzo di stdcall su Linux a 64 bit non è del tutto logico. Probabilmente funziona, dato che di solito c'è una sola convenzione di chiamata su un target a 64 bit, ma corretta, non lo è. Usa cdecl;

+0

Sicuramente chiamare i decoratori di convention è semplicemente ignorato per gli obiettivi x64. Se lo stesso codice viene compilato per x86, la convenzione di chiamata diventa nuovamente importante. –

+0

Beh, questo è esattamente quello che sto dicendo non è vero? Succede di lavorare è diverso dal corretto. –

+0

Ci si chiede cosa significa StdCallLibrary su Linux –

Problemi correlati