Reputation: 361
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
Reputation: 595827
Per the Java JNA documentation, a Java String
is converted to a const char*
when passed to native code:
Java
String
s perform the same function as the native typesconst char*
andconst 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 javaString
should be converted. JavaString
s are normally converted tochar*
since this is the most common usage of strings. Strings are automatically converted to a NUL-terminated array ofchar
across the function call. Returnedchar*
values are automatically copied into aString
if the method signature returnsString
(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 nativewchar_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