Search code examples
javadelphijava-native-interfacejna

JNA vs. DLL (Delphi)


I'm a new(bie) here but, I have a "big problem"

I Have a DLL (in Delphi) and I want to access it by Java. It's easy if you use a simple return or procedure in the "body" (.dpr) of DLL. But I need to use a Interface because I want to use a same code in a Desktop Application and a Web Application (using java). Code bellow:

TESTLIB.DLL

library TESTLIB;  
{$DEFINE TESTLIB}  

uses  
  System.SysUtils,  
  System.Classes,  
  TestInt in 'TestInt.pas';  

{$R *.res}  

function MyReturn(Test: PTest): PChar; stdcall;  
begin  
    Result := 'Im Here!';  
    Test^.vResult := 'Test 123';  
end;  

exports MyReturn;  

begin  
end.

Interface TestInt.pas

unit TestInt;  

interface  

type  
  PTest = ^TTest;  
  TTest = record  
    vResult: PChar;  
  end;  

  {$IFNDEF TESTELIB}  
  function MyReturn(Test: PTest): PChar; stdcall;  
  {$ENDIF}  

implementation  

{$IFNDEF TESTELIB}  
function MyReturn; external 'TESTLIB.DLL' name 'MyReturn';  
{$ENDIF}  

end.

and how I access this using a simple application in Delphi:

unit FormMain;  

interface  

uses  
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,   Vcl.Graphics,  
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;  

type  
  TForm1 = class(TForm)  
    Button1: TButton;  
    procedure Button1Click(Sender: TObject);  
  private  
    { Private declarations }  
  public  
    { Public declarations }  
  end;  

var  
  Form1: TForm1;  

implementation  

{$R *.dfm}  

uses TestInt;  //The interface

procedure TForm1.Button1Click(Sender: TObject);  
var  
  Test: TTest; //Type declared in Interface TestInt.pas  
begin  
    ShowMessage(MyReturn(@Test)); //Returns Im Here!  

    ShowMessage(Test.vResult); //Test 123  
end;  

end.

I want to access this using Java (JNI, JNA, etc... would be better if an example can be provided)

thanks a lot guys!


Solution

  • In general Delphi DLLs (using stdcall calling convention) are accessed in the same way that you would access Win32 APIs. You should be able to figure out appropriate type mappings by examining the sizes of the Delphi types and replacing them with similarly-sized Java types.

    PCHAR maps to a Java String, so your "interface" as provided would look something like this:

    public interface TestInt extends StdCallLibrary {
        TestInt INSTANCE = (TestInt)Native.loadLibrary("TestInt", TestInt.class);
    
        class Test extends Structure {
            public String vResult;
            public Test() { }
            public Test(Pointer p) { 
                super(p);
                read();
            }
            protected List getFieldOrder() { return Arrays.asList(new String[] { "vResult" }); }
        }
    
        String MyReturn(Test test);
    }
    

    Bear in mind David's warning about returning strings; if delphi allocates the memory, it must later free the memory, so it's usually easier to have the caller (Java) pass in allocated memory and have delphi simply write to the provided buffer.