Search code examples
javastringdelphidlljna

How to convert String to PWideString in Delphi for JNA consumption


I'm creating a DLL in Delphi and the method is like this:

function teste3 : PWideString; stdcall;
  var
    _string : string;
  begin
    _string := 'teste';
    Result := PWideString(_string);
  end;

Then i'm trying to call this method using JNA lib in JAVA. The code that call the method in JAVA is like this:

System.out.println(TDLL.getInstance().teste3());

But when the return of the call is only the char t the rest of the text is not comming.

How can I convert the String to PWideString without losing all the chars?

Obs: I tried returning PWideChar too but the result is the same.


Solution

  • Delphi's PWideString type is a pointer to a WideString. If you return such a pointer to Java, Java won't know what to do with it. You would need to return a raw PAnsiChar or PWideChar instead (prefer the latter, since Java strings are Unicode), but then you have an issue to deal with - memory management.

    If you return a pointer to a statically allocated string, or at least a dynamically allocated string that outlives the function, you can return a pointer to the character data directly:

    Delphi:

    const
      _string : WideString = 'teste'; // or UnicodeString in D2009+
    
    function teste3 : PWideChar; stdcall;
    begin
      Result := PWideChar(_string);
    end;
    

    Or:

    var
      _string : WideString; // or UnicodeString
    
    function teste3 : PWideChar; stdcall;
    begin
      _string = 'teste'
      Result := PWideChar(_string);
    end;
    

    Java:

    public class TDLL extends Library {
      TDLL INSTANCE = (TDLL) Native.loadLibrary("myjnalib.dll", TDLL.class);
      public static TDLL getInstance() {
        return INSTANCE;
      }
      WString teste3();
    }
    
    System.out.println(TDLL.getInstance().teste3().toString());
    

    However, if you need to dynamically allocate the string, you have to free that memory inside the same DLL or else it will be leaked. Java can't free the memory for you, since it doesn't know how the memory was allocated.

    To get around that, you should redesign the DLL function to accept a memory buffer as input, and then you can use JNA's Memory class on the Java side. Have the DLL fill the memory buffer, and then Java can call the Memory.getString() method to read the output data into a native Java string:

    Delphi:

    function teste3(buffer: PByte; bufsize: Integer) : Integer; stdcall;
    var
      _string : WideString; // or UnicodeString
      MaxChars: Integer;
    begin
      _string := 'teste';
      MaxChars := (bufsize div sizeof(WideChar)) - 1;
      StrLCopy(PWideChar(buffer), PWideChar(_string), MaxChars);
      Result := Min(Length(_string), MaxChars);
    end;
    

    Java:

    public class TDLL extends Library {
      TDLL INSTANCE = (TDLL) Native.loadLibrary("myjnalib.dll", TDLL.class);
      public static TDLL getInstance() {
        return INSTANCE;
      }
      int teste3(Memory buf, int bufsize);
    }
    
    Memory buf = new Memory(10);
    TDLL.getInstance().teste3(buf, buf.size());
    System.out.println(buf.getString(0, true));
    

    That being said, you might consider switching to JNI instead (see Programming JNI with Delphi), because then the DLL can use Java's own memory manager to dynamically allocate and return an actual Java string object, and then Java can free it normally as needed:

    Delphi:

    function Java_TDLL_test3(env: PJNIEnv; obj: JObject): JString; stdcall;
    var
      _string : WideString; // or UnicodeString
    begin
      _string := 'teste';
      Result := env^.NewString(env, PJChar(PWideChar(_string)), Length(_string));
    end;
    

    Java:

    public class TDLL {
      static {
        System.loadLibrary("myjnalib");
      }
    
      public native string test3();
    }
    
    System.out.println(new TDLL().teste3());