使用 JNA 和 Ada 的 Interface.C 包将 Ada 动态库与 Java 接口

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

我必须编写一个 DLL 来提供需要的简单服务:

从 C 的角度来看,签名应该是这样的:

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

出于多种原因选择在 Ada 中对库进行编码(多个 Ada 包重复使用,我们不想再次编码也不想重新设计(遗留))。 该库应该在 Java 项目中使用(我们不想从 Java 端重新编码 lib 提供的服务)。 (这样做的主要原因是库隐藏了复杂的 Ada 类型到 JSON + 协议库的转换。)

库以后还要和C或C++接口,所以主要思路还是靠基本类型。

如何同时连接到 C 和 Java?

(我找到了一个解决方案,所以我想在下面分享详细信息;查看我自己的答案。由于旧的工作场所网络浏览器非常错误,无法使用复选框来完成...)

以下是我如何使用 JNA 和 Ada 的 Interface.C 包设法将 Java 与 Ada 库连接起来。

dll 相当简单,此处提供的 Ada 和 Java 代码至少显示了 char* 作为输入或输出参数的 2 种用法。

可以阅读以下文档:JNA documentation, this Ada wiki, Ada standard doc and some other Ada standard doc.

N.B.: 请注意,以下 Java 代码适用于任何匹配的 C 接口声明。请参阅 post 末尾的建议 .h 文件。当然 Java 控制台结果将取决于 DLL 实现。

初步检查

您需要在 java 项目中包含 JNA.jar jar 和 JNA-Platform.jar。 下载请参阅 JNA GitHub

一定要使用一致的 Java 和 Ada lib 架构:即都是 32 位或都是 64 位。否则,JNA/Java 将无法加载库。

不要忘记使用以下 VM 选项 -Djna.debug_load=true 来查看 JNA 的登录控制台!

您的 Java 项目 /bin 文件夹应包含以下内容:

  • 生成的 Ada DLL(使用给定的代码,它将是 libadalib.dll
  • 你的libgnat-version.dll
  • 你的libgcc_s_seh-1.dll

首先,Ada部分:

请注意,您可能需要将一些 gnat DLL 放在您自己的 DLL 附近。我目前还没有设法将所有内容打包到我自己的 DLL 中。

因此您可能需要在生成的 dll 的同一文件夹中包含以下内容(即您的 Java 项目的 /bin 目录):

  • 你的 libgnat-version.dll
  • 你的libgcc_s_seh-1.dll

如果需要解决这个问题,请使用 DependancyWalker。 (参见 http://www.dependencywalker.com/

有一个 GPR 选项可以激活/停用自动 DLL 详细说明,但我还没有测试它。


艾达代码

Ada 库项目:

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 文件

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;

现在 Java 代码:

Class加载和映射服务到库:

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调用库

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 [=14=]
        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[=14=]";
        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()));

    }

}

来自 java 控制台的输出(JNA 调试为真)

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)

现在 Ada 库也可以使用匹配的 .h 文件轻松地与 C 或 C++ 接口:

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

如何在从 Eclipse 调用时调试库?

使用 Gnat Pro Studio (GPS),您可以进入调试器视图并将 gdb 附加到 java 应用程序进程的 PID(对于 windows)。 但是,如果没有下面的技巧,你将无法设置断点。

诀窍是在 DLL 中有一个无限循环(用于开发目的)。

正文:

while flag loop
    null;
end loop;

广告文件:

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

一旦 gdb 设法附加到 运行 DLL 代码(无限循环),gdb 就会崩溃。

在你的循环中放置一个断点,并在 gdb 中输入 c。 它会打破你的循环。

在代码的其他地方放置另一个断点,然后键入 follwong set flag := false,然后 c

现在 gdb 应该继续到下一个断点。

(或使用"n"(next)gdb指令自行调试。)