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.
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.
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.
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.
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.
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With