Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I call a Delphi function that returns a string using JNA?

I'm working on calling functions from a Delphi compiled *.so file from a Java program. After some research it seems like JNA is he way to go. Before diving into some complex Delphi code, I'm trying to play with some "Hello World" code but am having trouble getting a string returned by a Delphi function.

The Delphi code (helloworld.pp):

library HelloWorldLib;

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

exports HelloWorld;

begin
end.

I compile it from the command line with "fpc -Mdelphi helloworld.pp", which produces libhelloworld.so.

Now my Java class:

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

However when I run this Java code I get:

# 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

Note that if I change my Delphi method (and the associated Java interface) to return a hardcoded integer, everything works great: the string I pass gets printed and I get the int back as expected.

Strangely enough, if the Delphi method returns a char, I have to write my JNA proxy as returning a byte and cast it to char manually (if I declare my interface as returning a char it prints out a garbage character).

Any idea what is going wrong here?

FYI, I'm on Ubuntu 12.04, 64bits, using Sun JDK 1.7.0_10-b18, JNA 3.5.1 and Free Pascal Compiler version 2.4.4-3.1.

like image 255
Christophe L Avatar asked Dec 27 '12 15:12

Christophe L


People also ask

What is the return type of a function in Delphi?

A most common construct in a Delphi application would be a procedure or a function. Known as routines, procedures or functions are statement blocks you call from different locations in a program. Simply put a procedure is a routine not returning a value while a function returns a value. A return value from a function is defined by the return type.

What is a procedure in Delphi?

A most common construct in a Delphi application would be a procedure or a function. Known as routines, procedures or functions are statement blocks you call from different locations in a program. Simply put a procedure is a routine not returning a value while a function returns a value.

What are the different types of Delphi routine?

Further, Delphi routines can really have many faces: Routine, Method, Method Pointer, Event Delegate, Anonymous method... Can a Function Return Multiple Values? The first answer that comes to mind is no, simply because when we think of a function, we think of a single return value.

What is the use of string find() function in Java?

Returns an integer specifying the position of the first occurrence of one string within another, where the search starts at a specified position. Returns an integer specifying the position of the first occurrence of one string within another, where the search starts at a specified position.


1 Answers

A Delphi or FreePascal string is a managed type that cannot be used as a JNA type. The JNA documentation explains that Java String is mapped to a pointer to a null-terminated array of 8 bit characters. In Delphi terms that is PAnsiChar.

So you can change the input parameter in your Pascal code from string to PAnsiChar.

The return value is more problematic. You will need to decide who allocates the memory. And whoever allocates it must also free it.

If the native code is responsible for allocating it then you'd need to heap allocate the null-terminated string. And return a pointer to it. You'd also need to export a deallocator so that the Java code can ask the native code to deallocate the heap allocated block of memory.

It is usually more convenient to allocate a buffer in the Java code. Then pass that to the native code and let it fill out the content of the buffer. This Stack Overflow question illustrates the technique, using the Windows API function GetWindowText as its example: How can I read the window title with JNI or JNA?

An example of this using Pascal would be like so:

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;

On the Java side, I guess the code would look like this, bearing in mind that I know absolutely nothing about 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);
like image 131
David Heffernan Avatar answered Nov 15 '22 11:11

David Heffernan