网站首页 > 技术文章 正文
Java本地访问(JNA)是一个勇敢的开源尝试,通过一个更直观和易于使用的API来解决JNI的复杂性。作为一个第三方库,JNA必须作为依赖项添加到我们的项目中:
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>5.8.0</version>
</dependency>
接下来,让我们尝试调用问题137中相同的sumTwoInt()方法。这个函数定义在一个名为math.dll的C本地共享库中,并存储在我们的项目中的jna/cpp文件夹中。我们首先编写一个扩展JNA的Library接口的Java接口。这个接口包含我们计划从Java调用并在本地代码中定义的方法和类型的声明。我们编写包含sumTwoInt()声明的SimpleMath接口如下:
public interface SimpleMath extends Library {
long sumTwoInt(int x, int y);
}
接下来,我们必须指导JNA加载math.dll库并生成这个接口的具体实现,这样我们就可以调用它的的方法。为此,我们需要jna.library.path系统属性和JNA的Native类如下:
package modern.challenge;
public class Main {
public static void main(String[] args) {
System.setProperty("jna.library.path", "./jna/cpp");
SimpleMath math = Native.load(Platform.isWindows()
? "math" : "NOT_WINDOWS", SimpleMath.class);
long result = math.sumTwoInt(3, 9);
System.out.println("Result: " + result);
}
}
在这里,我们指导JNA通过System.setProperty()从jna/cpp加载math.dll,但您也可以从终端通过-Djna.library.path=jna/cpp来完成。接下来,我们调用Native.load(),它接受两个参数。首先,它接受原生库的名称,在我们的情况下是math(不带.dll扩展名)。其次,它接受包含方法声明的Java接口,在我们的情况下是SimpleMath.class。load()方法返回一个SimpleMath的具体实现,我们用它来调用sumTwoInt()方法。
JNA Platform助手允许我们提供特定于当前操作系统的原生库的名称。我们只有Windows的math.dll。
实现.cpp和.h文件 这次,.cpp和.h文件没有命名约定,所以让我们将它们命名为Arithmetic.cpp和Arithmetic.h(头文件是可选的)。Artihmetic.cpp的源代码基本上是纯C代码:
#include <iostream>
#include "Arithmetic.h"
long sumTwoInt(int x, int y) {
std::cout << "C++: The received arguments are : " << x <<
" and " << y << std::endl;
return (long)x + (long)y;
}
正如您所看到的,使用JNA,我们不需要用JNI特定的桥接代码来修补我们的代码。它只是纯C代码。Arithmetic.h是可选的,我们可以这样写:
#ifndef FUNCTIONS_H_INCLUDED
#define FUNCTIONS_H_INCLUDED
long sumTwoInt(int x, int y);
#endif
接下来,我们可以编译我们的代码。
编译C源代码 通过G++编译器和下图所示的命令完成C源代码的编译:
图7.5 - 编译C++代码
或者,作为纯文本:
C:\SBPBP\GitHub\Java-Coding-Problems-Second-Edition\Chapter07\P138_EngagingJNA>g++ -c "-I%JAVA_HOME%\include" "-I%JAVA_HOME%\include\win32" src/main/java/modern/challenge/cpp/Arithmetic.cpp –o jna/cpp/Arithmetic.o
接下来,我们可以生成适当的本地库。
生成本地共享库 是时候创建本地共享库math.dll了。为此,我们再次使用G++,如下图所示:
图7.6 - 生成math.dll
或者,作为纯文本:
C:\SBPBP\GitHub\Java-Coding-Problems-Second-Edition\Chapter07\P138_EngagingJNA>g++ -shared –o jna/cpp/math.dll jna/cpp/Arithmetic.o –static –m64 –Wl,--add-stdcall-alias
在这一点上,您应该在jna/cpp文件夹中有math.dll。
最后,运行代码 最后,我们可以运行代码。如果一切顺利,那么您就完成了。否则,如果您得到一个异常,如java.lang.UnsatisfiedLinkError: Error looking up function 'sumTwoInt': The specified procedure could not be found,那么我们必须修复它。但是,发生了什么?最有可能的是,G++编译器应用了一种称为名称混淆(或名称装饰)的技术 - https://en.wikipedia.org/wiki/Name_mangling。换句话说,G++编译器将sumTwoInt()方法重命名为了JNA不知道的其他名称。
解决这个问题可以分两步进行。首先,我们需要使用DLL依赖项查看器(例如这个)检查math.dll,https://github.com/lucasg/Dependencies。正如下图所示,G++已将sumTwoInt重命名为_Z9sumTwoIntii(当然,在您的计算机上可能是另一个名称):
图7.7 - G++已将sumToInt重命名为_Z9sumTwoIntii
其次,我们必须告诉JNA这个名称(_Z9sumTwoIntii)。基本上,我们需要定义一个包含名称对应映射的Map,并将这个map传递给接受这个map作为最后一个参数的Native.load()的一个变体。代码很直接:
public class Main {
private static final Map MAPPINGS;
static {
MAPPINGS = Map.of(
Library.OPTION_FUNCTION_MAPPER,
new StdCallFunctionMapper() {
Map<String, String> methodNames
= Map.of("sumTwoInt", "_Z9sumTwoIntii");
@Override
public String getFunctionName(
NativeLibrary library, Method method) {
String methodName = method.getName();
return methodNames.get(methodName);
}
});
}
public static void main(String[] args) {
System.setProperty("jna.library.path", "./jna/cpp");
SimpleMath math = Native.load(Platform.isWindows()
? "math" : "NOT_WINDOWS", SimpleMath.class, MAPPINGS);
long result = math.sumTwoInt(3, 9);
System.out.println("Result: " + result);
}
}
完成!现在,您应该得到3+9的结果。请随时进一步探索JNA,并尝试使用C/C++结构体、联合体和指针。
猜你喜欢
- 2024-10-27 从bitmap到布隆过滤器,再到高并发缓存设计策略
- 2024-10-27 强大 WebView2 + 不用写 JavaScript 的 htmx.js 「小轻快」开发桌面程序
- 2024-10-27 《JSP》第13节:JSP中的四大作用域介绍
- 2024-10-27 Java,FreeMarker,模板引擎,通过案例代码,学懂模板引擎
- 2024-10-27 面向对象的三大特性(c++面向对象的三大特性)
- 2024-10-27 教你分析9种 OOM 常见原因及解决方案
- 2024-10-27 可动态调节参数的线程池实现(动态调试工具有哪些)
- 2024-10-27 Java,基本类型和引用类型,强引用、软引用、弱引用、虚引用
- 2024-10-27 深入理解Java:类加载机制及反射(java常见类加载器)
- 2024-10-27 JVM系列-6.javap指令介绍(jvm调优)
- 02-21走进git时代, 你该怎么玩?_gits
- 02-21GitHub是什么?它可不仅仅是云中的Git版本控制器
- 02-21Git常用操作总结_git基本用法
- 02-21为什么互联网巨头使用Git而放弃SVN?(含核心命令与原理)
- 02-21Git 高级用法,喜欢就拿去用_git基本用法
- 02-21Git常用命令和Git团队使用规范指南
- 02-21总结几个常用的Git命令的使用方法
- 02-21Git工作原理和常用指令_git原理详解
- 最近发表
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)