Search code examples
javaarraysdelphidlljna

Passing an array from a Delphi DLL to JNA


I'm using the Java JNA library to call a Delphi DLL that I have created. The Delphi function I'm using returns a type that is an array of PAnsiChar. The problem I'm getting is that when I try and call that function in Java, it's giving me a java.lang.Error: Invalid memory access.

My Delphi code is here:

function doTest(inputStatement: PAnsiChar): TDynamicAnsiCharArray; stdcall;
begin
  SetLength(result, 3);

  result[0] := 'Line 1';
  result[1] := 'Line 2';
  result[2] := 'Line 3';
end;

My Java code is here:

public interface CLib extends StdCallLibrary {

    CLib INSTANCE = (CLib) Native.loadLibrary("DatabaseLibrary", CLib.class);
    public String[] doTest(String input);
}

public Main() {

    String[] dllOut = CLib.INSTANCE.doTest("Test?");
    for(int i = 0; i < dllOut.length; i++){
        System.out.println(dllOut[i]);
    }
}

The full Java error is here:

Exception in thread "main" java.lang.Error: Invalid memory access
at com.sun.jna.Native.invokePointer(Native Method)
at com.sun.jna.Function.invokePointer(Function.java:470)
at com.sun.jna.Function.invoke(Function.java:430)
at com.sun.jna.Function.invoke(Function.java:315)
at com.sun.jna.Library$Handler.invoke(Library.java:212)
at com.sun.proxy.$Proxy0.doTest(Unknown Source)
at Main.<init>(Main.java:17)
at Main.main(Main.java:25)

Line 17 is the line with the String[] definition.

I have this feeling that it's not going to work at all this way, but I'm hopeful there is actually a way.


Solution

  • You did not let us know what TDynamicAnsiCharArray is but I presume it is a dynamic array of PAnsiChar:

    type
      TDynamicAnsiCharArray = array of PAnsiChar;
    

    That is not a valid type for binary interop.

    On the Java side, you cannot use String[] as a return value, for much the same reason.

    There are lots of ways you might tackle this. None is particularly simple. I think that perhaps the cleanest is to ask the function to return a single string containing the entire list. You might use something crude like double null-terminated strings. Or you might serialize the list to a JSON array and return that text. For either of those options you just need to find a way to return a string.

    The cleanest way to do that is to have the caller allocate the memory. This answer covers that technique: How can I call a Delphi function that returns a string using JNA?

    An alternative to having the caller allocate the memory is to use a string type that is allocated off a shared heap. The obvious choice is the COM BSTR type, WideString in Delphi. That is represented as WTypes.BSTR in JNA. Be careful not to use that as a WideString function return value though because Delphi does not follow the platform ABI: Why can a WideString not be used as a function return value for interop?