1

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.

mjn
  • 36,362
  • 28
  • 176
  • 378
JamEngulfer
  • 747
  • 4
  • 11
  • 33
  • 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 Nov 11 '14 at 11:54
  • Maybe http://stackoverflow.com/q/10158582/80901 is helpful, it uses the [Pointer](https://jna.java.net/javadoc/com/sun/jna/Pointer.html) JNA class – mjn Nov 11 '14 at 11:59
  • Just pointing this out, but I was able to pass a single `PAnsiChar` from the dll back to Java. – JamEngulfer Nov 11 '14 at 11:59
  • 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 variable – JamEngulfer Nov 11 '14 at 12:08
  • Ok, I see you aren't passing back Delphi string. Still, arrays won't fly in that direction. Lots of possible solutions. – David Heffernan Nov 11 '14 at 13:34
  • Well, I'm time restricted and don't particularly want to spend forever working stuff out. I've realised I can just pass one massive PAnsiString with separation characters for each line. It's not elegant, but it's what I'm settling for. Thanks for the help though! – JamEngulfer Nov 11 '14 at 13:47
  • 1
    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 Nov 11 '14 at 14:08

1 Answers1

0

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?

Community
  • 1
  • 1
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490