Interface Ada dynamic Library with Java using JNA and Ada's Interface.C packages

前端 未结 1 1910
有刺的猬
有刺的猬 2020-12-12 03:26

I have to write a DLL is supposed to provide a simple service that takes:

  • IN a string or byte array (equivalent of char*)
  • IN integer expliciting the s
相关标签:
1条回答
  • 2020-12-12 03:54

    Here is how I managed to interface Java with an Ada Library using the JNA and Ada's Interface.C packages.

    The dll is rather simple, and the Ada and Java code provided here show at least 2 usages of char* as in or out parameters.

    One can read the following docs : JNA documentation, this Ada wiki, Ada standard doc and some other Ada standard doc.

    N.B.: Note that the following Java code would work with any matching C interface declaration. See the proposed .h file at end of post. Of course Java console result would depend on DLL implementation.

    Preliminary checks

    You'll need to include in your java project the JNA.jar jar and the JNA-Platform.jar. see JNA GitHub for downloads.

    Be sure to use a consistent Java and Ada lib architecture : i.e. both 32 bits or both 64 bits. Otherwise, JNA/Java won't be able to load the library.

    Don't also forget to use the following VM option -Djna.debug_load=true to see the JNA's log in console !

    Your Java's project /bin folder shall contain the following:

    • the Ada DLL produced (with the given code, it will be libadalib.dll)
    • your libgnat-version.dll
    • your libgcc_s_seh-1.dll

    First, the Ada part:

    Please note that you may need to put some gnat DLLs near your own one. I currently have not yet managed to pack everything in my own DLL.

    So you may need to have the following in same folder of the produced dll (I.e. your Java project's /bin directory):

    • your libgnat-version.dll
    • your libgcc_s_seh-1.dll

    Use DependancyWalker if needed to figure this out. (see http://www.dependencywalker.com/)

    There is a GPR option to activate / deactivate automatic DLL elaboration, but I do not have tested it yet.


    Ada code

    Ada library project:

    project adalib is
        for Languages use ("Ada");
        for Source_Dirs use (project'Project_Dir & "./src");
        for Library_Kind use "dynamic"; -- for DLL
        for Library_Name use project'Name; -- will produce "libadalib.dll"
        for Library_Interface use ("ada_interface");
        for Library_Dir use project'Project_Dir & "./dll";
        for Library_Src_Dir use project'Project_Dir & "./dll";
        -- include other DLL / .a here if needed
        -- for Library_Options use ("-L" & path_to_lib,
        --                          "-l" & path_to_lib  
        --                         );
        -- define your favorite compiler, builder, binder, linker options
    end adalib;
    

    ./src ada files

    ada_interface.ads

    pragma Ada_2012;
    
    with Interfaces.C;
    with Interfaces.C.Strings;
    
    
    package ada_interface is
    
        procedure myService (
                             inBuffer       : in     Interfaces.C.Strings.chars_ptr; -- as in
                             anInteger      : in     Interfaces.C.int;
                             outBuffer      : in     Interfaces.C.Strings.chars_ptr; -- as out buffer
                             outBufferSize  : in     Interfaces.C.int;               -- max out buffer size
                             usedBufferSize :    out Interfaces.C.int
                            );
        pragma Export (Convention    => C,
                       Entity        => myService,
                       External_Name => "Renamed_myService");
    end ada_interface;
    

    ada_interface.adb

    pragma Ada_2012;
    
    
    with Ada.Text_IO;
    
    with Interfaces.C.Strings;
    
    package body ada_interface is
    
        procedure myService (
                             inBuffer       : in     Interfaces.C.Strings.chars_ptr; -- as in
                             anInteger      : in     Interfaces.C.int;
                             outBuffer      : in     Interfaces.C.Strings.chars_ptr; -- as out buffer
                             outBufferSize  : in     Interfaces.C.int;               -- max out buffer size
                             usedBufferSize :    out Interfaces.C.int
                            )
        is
           -- if elaboration needs to be explicitly called
           procedure ada_elaboration;
           pragma import (C, ada_elaboration, "adalibinit"); -- "<name of lib>init". May not be needed with proper options in GPR
    
           Required_Length : Natural := Natural (outBufferSize);
           myString : String := "This is a sample string";
           use type Interfaces.C.size_t;
        begin
    
           ada_elaboration;
    
           --
           Ada.Text_IO.Put_Line ("======= inside myService");
    
           -- print the string given by char*
           Ada.Text_IO.Put_Line (Interfaces.C.Strings.Value (inBuffer));
           -- the int
           Ada.Text_IO.Put_Line (Natural'Image (Natural (anInteger)));
    
           -- current value of the char* to be used as OUT buffer
           Ada.Text_IO.Put_Line (Interfaces.C.Strings.Value (outBuffer));
    
           -- use the char* to be used as out
           Interfaces.C.Strings.Update
             (Item   => outBuffer,
              Offset => 0,
              Str    => myString & Interfaces.C.To_Ada (Interfaces.C.nul), -- "& Interfaces.C.To_Ada(Interfaces.C.nul)" is equivalent to "& Character'Val(0)"
              Check  => false);
    
           usedBufferSize := Interfaces.C.int (Interfaces.C.Strings.Strlen (outBuffer) - 1); -- see later java code and traces
    
        end myService;
    
    end ada_interface;
    

    Now the Java code:

    Class to load and map services to the library:

    package tst;
    
    import com.sun.jna.Library;
    import com.sun.jna.Native;
    import com.sun.jna.Pointer;
    import com.sun.jna.ptr.IntByReference;
    
    public class Ada_Lib {
    
        public interface My_Ada_Lib extends Library {
    
            My_Ada_Lib instance = (My_Ada_Lib) Native.loadLibrary("libadalib", My_Ada_Lib.class);
            My_Ada_Lib synchronizedInstance = (My_Ada_Lib) Native.synchronizedLibrary(instance);
    
        void Renamed_myService (
                            Pointer inBuffer,
                            int anInteger,
                            byte[] outBuffer,
                            int outBufferSize,
                            IntByReference usedBufferSize
                           );
        }
    }
    

    Class to call the library

    package tst;
    
    import com.sun.jna.Memory;
    import com.sun.jna.Native;
    import com.sun.jna.Pointer;
    import com.sun.jna.ptr.IntByReference;
    
    import tst.Ada_Lib.My_Ada_Lib;
    
    public class TestMyLib {
    
        private static My_Ada_Lib theLib = Ada_Lib.My_Ada_Lib.synchronizedInstance;
    
        public static void main(String[] args) {
    
            String lorem = "Sin autem ad adulescentiam perduxissent, dirimi tamen interdum contentione vel uxoriae condicionis vel commodi alicuius, quod idem adipisci uterque non posset. Quod si qui longius in amicitia provecti essent, tamen saepe labefactari, si in honoris contentionem incidissent; pestem enim nullam maiorem esse amicitiis quam in plerisque pecuniae cupiditatem, in optimis quibusque honoris certamen et gloriae; ex quo inimicitias maximas saepe inter amicissimos exstitisse." +
                    "\n" +
                    "Quanta autem vis amicitiae sit, ex hoc intellegi maxime potest, quod ex infinita societate generis humani, quam conciliavit ipsa natura, ita contracta res est et adducta in angustum ut omnis caritas aut inter duos aut inter paucos iungeretur." +
                    "\n" +
                    "Haec subinde Constantius audiens et quaedam referente Thalassio doctus, quem eum odisse iam conpererat lege communi, scribens ad Caesarem blandius adiumenta paulatim illi subtraxit, sollicitari se simulans ne, uti est militare otium fere tumultuosum, in eius perniciem conspiraret, solisque scholis iussit esse contentum palatinis et protectorum cum Scutariis et Gentilibus, et mandabat Domitiano, ex comite largitionum, praefecto ut cum in Syriam venerit, Gallum, quem crebro acciverat, ad Italiam properare blande hortaretur et verecunde.\n";
    
            // in params
            int inputInt = 25;
    
            Pointer p_Lorem = new Memory(lorem.length()+1); // +1 for C's \0
            p_Lorem.setString(0, lorem); // offset 0, no need to start at another offset
    
            // in param but to used for out buffer
            String stubOut = "Hello World ! 0123456789\0";
            int maxBufferSize = (stubOut.length());
    
            byte[] buffer = new byte[maxBufferSize];
            buffer = stubOut.getBytes();
    
            IntByReference usedBufferSize = new IntByReference(0); // any value works, since it is used as out param
    
    
            System.out.println("-------------------- Call to Lib ----------------------------");
            // call the lib !
            theLib.Renamed_myService(p_Lorem, inputInt, buffer, maxBufferSize, usedBufferSize);
    
            System.out.println("--------------------- Back to java --------------------------");
            System.out.println("In Java: used buffer size         = " + usedBufferSize.getValue());
            System.out.println("In Java: read outBuffer as String = " + Native.toString(buffer));
            System.out.println("In Java: read outBuffer as String with returned used buffer size = " + new String(buffer,0,usedBufferSize.getValue()));
    
        }
    
    }
    

    Output from a java console (with JNA debug to true)

    Looking in classpath from sun.misc.Launcher$AppClassLoader@4e0e2f2a for /com/sun/jna/win32-x86-64/jnidispatch.dll
    Found library resource at jar:file:/ [...]
    Looking for library 'libadalib'
    Adding paths from jna.library.path: null
    Trying libadalib.dll
    Adding system paths: []
    Trying libadalib.dll
    Looking for lib- prefix
    Trying liblibadalib.dll
    Looking in classpath from sun.misc.Launcher$AppClassLoader@4e0e2f2a for libadalib
    Found library resource at file:/<path>/TestMyLib/bin/libadalib.dll
    Looking in <path>\TestMyLib\bin\libadalib.dll
    Found library 'libadalib' at <path>\TestMyLib\bin\libadalib.dll
    -------------------- Call to Lib ----------------------------
    ======= inside myService
    Sin autem ad adulescentiam perduxissent, dirimi tamen interdum contentione vel uxoriae condicionis vel commodi alicuius, quod idem adipisci uterque non posset. Quod si qui longius in amicitia provecti essent, tamen saepe labefactari, si in honoris contentionem incidissent; pestem enim nullam maiorem esse amicitiis quam in plerisque pecuniae cupiditatem, in optimis quibusque honoris certamen et gloriae; ex quo inimicitias maximas saepe inter amicissimos exstitisse.
    Quanta autem vis amicitiae sit, ex hoc intellegi maxime potest, quod ex infinita societate generis humani, quam conciliavit ipsa natura, ita contracta res est et adducta in angustum ut omnis caritas aut inter duos aut inter paucos iungeretur.
    Haec subinde Constantius audiens et quaedam referente Thalassio doctus, quem eum odisse iam conpererat lege communi, scribens ad Caesarem blandius adiumenta paulatim illi subtraxit, sollicitari se simulans ne, uti est militare otium fere tumultuosum, in eius perniciem conspiraret, solisque scholis iussit esse contentum palatinis et protectorum cum Scutariis et Gentilibus, et mandabat Domitiano, ex comite largitionum, praefecto ut cum in Syriam venerit, Gallum, quem crebro acciverat, ad Italiam properare blande hortaretur et verecunde.
    
     25
    Hello World ! 0123456789
    --------------------- Back to java --------------------------
    In Java: used buffer size         = 22
    In Java: read outBuffer as String = This is a sample string // reads the full buffer
    In Java: read outBuffer as String with returned used buffer size = This is a sample strin // reads a length of 22 (so the 'g' is missing)
    

    Now the Ada library can also be easily interfaced with C or C++, using a matching .h file:

    void myService (char* inBuffer,  // as in string
                    int   anInteger, // as in param
                    char* outBuffer, // used as out buffer, but initalized by calling code
                    int   outBufferSize, // the initaliaed size
                    int   usedBufferSize // used as out param, the actually used size
    );
    

    How to debug the lib while called from Eclipse ?

    Using Gnat Pro Studio (GPS), you can go in the debugger view and attach gdb to the PID (for windows) of your java application process. However, without the following trick, you won't be able to set breakpoints.

    The trick is to have an infinite loop inside the DLL (for dev purposes).

    body:

    while flag loop
        null;
    end loop;
    

    ads file:

    flag : boolean := true; -- in private part     
    

    Once the gdb manages to attach to the running DLL code (infinite loop), the gdb will break.

    Put a breakpoint in your loop, and the type in gdb c. It will break in your loop.

    Put another breakpoint elsewhere in code, then type the follwong set flag := false, and then c.

    Now the gdb should continue to next breakpoint.

    (Or use the "n" (next) gdb instruction to debug at your own will.)

    0 讨论(0)
提交回复
热议问题