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

我必须写一个DLL应该提供一个简单的服务,需要:

  • 在字符串或字节数组中(相当于char *)
  • IN整数表示in char *的大小
  • IN等效于库中用于写入数据的char *缓冲区
  • 用于写入的可用char *缓冲区的大小
  • 将有效的写入大小输出到out char *缓冲区

使用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连接?

(我找到了一个解决方案,所以我想分享下面的详细信息;看看我自己的答案。 由于旧的工作场所webbrowser非常错误,无法使用复选框…

以下是我使用JNA和Ada的Interface.C软件包将Java与Ada Library连接起来的方法。

dll相当简单,这里提供的Ada和Java代码显示至少2个char*用作in或out参数。

可以阅读以下文档: JNA文档 , 此Ada wiki , Ada标准文档和其他一些Ada标准文档 。

注意:请注意,以下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的project / bin文件夹应包含以下内容:

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

首先,Ada部分:

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

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

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

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

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


阿达码

阿达图书馆项目:

 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"); -- "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代码:

要加载和映射服务到库的类:

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

要调用库的类

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

从java控制台输出(JNA调试为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://TestMyLib/bin/libadalib.dll Looking in \TestMyLib\bin\libadalib.dll Found library 'libadalib' at \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) 

现在,使用匹配的.h文件,Ada库也可以轻松地与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调用时调试lib?

使用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”(下一个)gdb指令根据自己的意愿进行调试。)