1
votes

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.

1
You can't do it like this. You cannot return Delphi strings across an interop boundary. Nor can you return dynamic arrays. You'll need to find another way. Typically this will involve the caller allocating memory.David Heffernan
Maybe stackoverflow.com/q/10158582/80901 is helpful, it uses the Pointer JNA classmjn
Just pointing this out, but I was able to pass a single PAnsiChar from the dll back to Java.JamEngulfer
I've looked at what seems like nearly every Stack Overflow question on this topic and I can't seem to find ANY that properly show how to pass an array of data in some way between these two. From what I've seen, you can declare an array in Java, then pass it as a parameter to Delphi, which populates it and passes it back up to Java. However, I can't find how to actually get that data into a java variableJamEngulfer
If you want to settle for that, it's up to you, but you probably are doing it wrongly. How do you ensure that the buffer that you allocate lives beyond the life of the function? How does the caller deallocate the memory allocated by the function?David Heffernan

1 Answers

0
votes

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?