Johan
Johan

Reputation: 361

"Invalid memory access" when calling Delphi DLL from Java

We have a DLL, written in Delphi, being called by a Java app. Initially we had issues when using PChar or ShortString but we changed these to PAnsiChar and all our issues seemed to have been solved.

However, when we started deploying the DLL to our clients, about 50% of the installations get the following error: Invalid memory access.

The very first line in the DLL is to write to our log file but that is not happening which indicated there is a problem between the Delphi and Java datatypes. Does anybody have any ideas as to what Delphi and Java data types work well together?

Delphi DLL code:

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

exports
  HasCOMConnection;

Calling from Java:

    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;
        }
    }

Upvotes: 2

Views: 186

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 595827

Per the Java JNA documentation, a Java String is converted to a const char* when passed to native code:

Java Strings perform the same function as the native types const char* and const wchar_t* (NUL-terminated arrays). In order to use the proper type when calling a native function, we have to introduce some sort of annotation to identify how the java Stringshould be converted. Java Strings are normally converted to char* since this is the most common usage of strings. Strings are automatically converted to a NUL-terminated array of characross the function call. Returned char* values are automatically copied into a String if the method signature returns String (strdup, for example).

So the use of PAnsiChar on the Delphi side is correct when passing a String as-is.

However, Delphi strings in Delphi 2009+ are natively encoded in UTF-16, same as Java strings. So, it would be more efficient (or at least, no risk of data loss) to use WString on the Java side:

The WString class is used to identify wide character strings. Unicode values are copied directly from the Java char array to a native wchar_t array.

And use PWideChar on the Delphi side to match, eg:

function HasCOMConnection(COMServerName: PWideChar): Boolean; stdcall;
private interface IPMOProcessLabResult extends com.sun.jna.Library {
    boolean HasCOMConnection(WString COMServerName);
}

That being said, there are 2 other problems with your code.

Per the same JNA documentation, a Java boolean maps to a native int, not a bool, so your Delphi code needs to use Integer (or Int32) or better LongBool, eg:

function HasCOMConnection(COMServerName: PAnsiChar{or PWideChar}): LongBool; stdcall;

More importantly, if a native library uses the stdcall calling convention, you have to extend IPMOProcessLabResult from com.sun.jna.win32.StdCallLibrary, eg:

private interface IPMOProcessLabResult extends com.sun.jna.StdCallLibrary

Otherwise, if you extend from com.sun.jna.Library then you need to use cdecl on the native side:

function HasCOMConnection(COMServerName: PAnsiChar{or PWideChar}): LongBool; cdecl;

Upvotes: 4

Related Questions