Call DLL written in Delphi from Java

Viewed 247

I have a simple DLL written in Delphi 10.4. When I call the DLL from another Delphi application, everything works 100%, but when I call the same DLL from a Java application (using JNA) I get strange results.

I initially declared my DLL input parameters of type PChar. When I call the DLL from the Java app, I get funny characters inside my DLL. I changed it to ShortString, but then I loose the first character of the string in my DLL. I probably need to do some datatype casting in Java but I cannot find out what. Can somebody perhaps help?

Here is a sample DLL in Delphi to demonstrate:

procedure TestDataTypes(PCharVar: PChar; ShortStringVar: ShortString); stdcall;
begin
  ShowMessage('PChar: ' + PCharVar + #13#10 +
              'ShortString: ' + ShortStringVar);
end;

exports
  TestDataTypes;

Code to call DLL from Delphi:

procedure TestDataTypes(PCharVar: PChar; ShortStringVar: ShortString); stdcall; external 'TestDataType.dll';
...
TestDataTypes(PChar('PChar Value'), 'ShortString Value');

Code to call DLL from Java:

INSTANCE.TestDataTypes("Java PChar Value", "Java ShortString Value");

Results: Top is when DLL is called from Delphi and bottom when it is called from Java: enter image description here

Some Feedback: I changed the datatype in Delphi to PAnsiChar and it works on my local pc. When deployed, it worked on some client machines but not others. The error I get when I call the DLL from my Java app is "Invalid memory access". I have added logging in my DLL and it seems it does not even enter the DLL.... I have also changed the Java datatype to WString but that did not make a difference.

Delphi code:

function HasCOMConnection(COMServerName: PAnsiChar): Boolean; stdcall;
begin
  WriteLog('HasCOMConnection: 64-Bit DLL entered');
  Result := HasConnection(COMServerName);
end;

Java Call:

    private interface IPMOProcessLabResult extends com.sun.jna.Library {
        boolean HasCOMConnection(String COMServerName);
    }

    private boolean canConnectToCOMServer() {
        try {
            IPMOProcessLabResult lib = (IPMOProcessLabResult) Native.loadLibrary(config.libraryName, IPMOProcessLabResult.class);
            return lib.HasCOMConnection(config.comServerName);
        }
        catch (Exception ex) {
            new AppendLog(new Date(), this.getClass() + "\t" + ex.getClass() + "\t" + "Exception while trying to connect to COMServer: " + ex.getMessage(), "debug");
            return false;
        }
    }
2 Answers

PChar is only an alias, which was changed to PWideChar with Delphi 2009 (let's just say, standard string and character types were changed to Unicode)! So every character does now use 2 Bytes.

So in order to make your DLL working as it should, you should change PChar to PAnsiChar.
Or you have to make some changes to your Java app so it does use some two-byte-character type. But I have no Java experience, so I can't help you with that.

And forget about shortstring, this type is not compatible to other languages!

See this site for more references about how to use and not to use DLLs.

ShortString is an old Delphi type that holds an ANSI string and stores the length in the first byte. You shouldn't use it.

PChar, in recent versions of Delphi, is an alias for PWideChar.

You are using JNA to call native code from Java.

A Java String internally uses UTF-16.

JNA uses the following rules:

  • A String is converted to PAnsiChar
  • A WString is converted to PWideChar

So, if you want to pass a Unicode string to the DLL, you should declare the Java method like this:

public void TestString(WString text);

And in Delphi:

procedure TestString(Text: PChar); stdcall;
begin
    ShowMessage(Text);
end;

And call the function like this:

INSTANCE.TestString(new WString("A Unicode string"));
Related