【发布时间】:2011-05-11 11:11:29
【问题描述】:
如何从 Java 调用 c 函数。 似乎 c 是基于编译器的。
我想在 Windows 中从 Java 调用 C 函数,并且 GCC 函数也来自 Java。
有参考吗?
【问题讨论】:
-
你可能想看看JNI(Java Native Interface)。
-
2011 年 5 月 11 日 11:11 提问 :-)
如何从 Java 调用 c 函数。 似乎 c 是基于编译器的。
我想在 Windows 中从 Java 调用 C 函数,并且 GCC 函数也来自 Java。
有参考吗?
【问题讨论】:
看看Java Native Interface: Getting Started。
2.1 概述
[...] 编写一个调用 C 函数进行打印的简单 Java 应用程序 “你好世界!”。该过程包括以下步骤:
创建一个声明本机方法的类 (HelloWorld.java)。采用 javac编译HelloWorld源文件,生成类 文件 HelloWorld.class。 javac 编译器随 JDK 或 Java 提供 2 个 SDK 版本。使用
javah -jni生成C头文件 (HelloWorld.h) 包含本机方法的函数原型 执行。 javah 工具随 JDK 或 Java 2 SDK 一起提供 发布。编写原生的C实现(HelloWorld.c) 方法。将 C 实现编译为本机库,创建Hello-World.dll或libHello-World.so。使用 C 编译器和链接器 在宿主环境中可用。运行 HelloWorld 程序,使用 java运行时解释器。两个类文件 (HelloWorld.class) 并加载本机库(HelloWorld.dll或libHelloWorld.so) 在运行时。本章的其余部分解释了这些步骤 详细。2.2 声明本机方法
您首先在 Java 编程中编写以下程序 语言。该程序定义了一个名为 HelloWorld 的类,其中包含一个 本机方法,打印。
class HelloWorld { private native void print(); public static void main(String[] args) { new HelloWorld().print(); } static { System.loadLibrary("HelloWorld"); } }HelloWorld 类定义以 print 本机方法的声明开始。接下来是一个主要方法 实例化 Hello-World 类并调用 print 本机方法 对于这个例子。类定义的最后一部分是静态的 加载包含 打印本机方法的实现。
native 方法的声明有两个区别 比如 print 和 Java 中常规方法的声明 编程语言。本机方法声明必须包含 本机修饰符。 native修饰符表示这个方法是 用另一种语言实现。此外,本机方法声明 以分号结尾,即语句终止符, 因为类中没有本地方法的实现 本身。我们将在单独的 C 文件中实现 print 方法。
在调用本地方法 print 之前,本地库 必须加载工具打印。在这种情况下,我们加载原生
HelloWorld类的静态初始化程序中的库。爪哇 虚拟机在之前自动运行静态初始化程序 调用HelloWorld类中的任何方法,从而确保 在调用 print 本机方法之前加载本机库。我们定义了一个能够运行
HelloWorld类的main 方法。Hello-World.main调用本机方法 print 的方式与 它会调用一个常规方法。
System.loadLibrary采用库名称,定位一个本地库 对应于该名称,并将本机库加载到 应用。我们将在后面讨论确切的加载过程 书。现在只需记住,为了System.loadLibrary("HelloWorld")要成功,我们需要创建一个 本机库在 Win32 上称为HelloWorld.dll,或在 Win32 上称为libHelloWorld.so索拉里斯。2.3 编译HelloWorld类
定义 HelloWorld 类后,将源代码保存在 名为 HelloWorld.java 的文件。然后使用编译源文件 JDK 或 Java 2 SDK 版本附带的 javac 编译器:
javac HelloWorld.java这个命令会生成一个
HelloWorld.class当前目录下的文件。2.4 创建原生方法头文件
接下来我们将使用
javah工具生成JNI风格的头文件 这在用 C 实现本机方法时很有用。您可以运行javah上的Hello-World类如下:javah -jni HelloWorld头文件的名字就是类名 在其末尾附加“
.h”。上面显示的命令 生成一个名为HelloWorld.h的文件。我们不会列出生成的 完整的头文件在这里。最重要的部分 头文件是Java_HelloWorld_print的函数原型,它 是实现 HelloWorld.print 方法的 C 函数:JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *, jobject);暂时忽略
JNIEXPORT和JNICALL宏。你可能已经注意到 本机方法的 C 实现接受两个参数 即使本机方法的相应声明接受 没有论据。每个本机方法的第一个参数 实现是一个JNIEnv接口指针。第二个参数是 引用HelloWorld对象本身(有点像“this” C++ 中的指针)。我们将讨论如何使用JNIEnv接口 指针和本书后面的jobject参数,但这个简单 示例忽略这两个参数。2.5 编写本机方法实现
javah生成的 JNI 风格的头文件帮助你编写 C 或 本机方法的 C++ 实现。你写的函数 必须遵循生成的头文件中指定的 -prototype。你 可以在C文件HelloWorld.c中实现Hello-World.print方法为 如下:#include <jni.h> #include <stdio.h> #include "HelloWorld.h" JNIEXPORT void JNICALL Java_HelloWorld_print(JNIEnv *env, jobject obj) { printf("Hello World!\n"); return; }这个本地方法的实现很简单。它使用 printf 函数显示字符串“Hello World!”然后返回。如前所述,
JNIEnv指针和对对象的引用这两个参数都被忽略了。C程序包括三个头文件:
jni.h-- 这个头文件提供了本地代码需要的信息 调用 JNI 函数。编写本机方法时,您必须始终 将此文件包含在您的 C 或 C++ 源文件中。stdio.h-- 代码 上面的 sn-p 还包括stdio.h,因为它使用了printf功能。HelloWorld.h-- 您使用生成的头文件javah。它包括Java_HelloWorld_print的 C/C++ 原型 功能。 2.6 编译C源码并创建Native库请记住,当您在
HelloWorld.java文件,您包含了一行代码,该代码加载了本机 库进入程序:System.loadLibrary("HelloWorld");现在所有必要的 C 代码 写好了,需要编译
Hello-World.c,构建这个native 图书馆。不同的操作系统支持不同的方式构建原生 图书馆。在 Solaris 上,以下命令构建一个共享库 调用 libHello-World.so:
cc -G -I/java/include -I/java/include/solaris HelloWorld.c -o libHelloWorld.so-G 选项指示 C 编译器生成共享库而不是常规 Solaris 可执行文件。由于本书页宽的限制, 我们将命令行分成两行。你需要输入命令 在一行中,或将命令放在脚本文件中。在
Win32上, 以下命令构建动态链接库 (DLL)HelloWorld.dll使用 Microsoft Visual C++ 编译器:cl -Ic:\java\include -Ic:\java\include\win32 -MD -LD HelloWorld.c -FeHelloWorld.dll
-MD选项确保HelloWorld.dll与Win32多线程C 库链接。-LD选项指示 C 编译器生成 DLL 而不是 常规 Win32 可执行文件。当然,在 Solaris 和 Win32 上,你 需要放入反映您自己设置的包含路径 机器。2.7 运行程序
此时,您已经准备好运行程序的两个组件。 类文件 (
HelloWorld.class) 调用本地方法,并且 本机库 (Hello-World.dll) 实现本机方法。因为
HelloWorld类包含了自己的main方法,所以可以运行 Solaris或Win32上的程序如下:java HelloWorld您应该会看到以下输出:
Hello World!设置原生库路径很重要 正确地让您的程序运行。本机库路径是一个列表 Java 虚拟机在加载时搜索的目录 本机库。如果您没有设置本机库路径 正确,然后您会看到类似于以下内容的错误:
java.lang.UnsatisfiedLinkError: no HelloWorld in library path at java.lang.Runtime.loadLibrary(Runtime.java) at java.lang.System.loadLibrary(System.java) at HelloWorld.main(HelloWorld.java)确保本机库位于本机库路径中的目录之一中。 如果您在 Solaris 系统上运行,
LD_LIBRARY_PATH环境变量用于定义本机库路径。制作 确保它包含包含libHelloWorld.so文件。如果libHelloWorld.so文件在当前 目录下,可以在标准中发出以下两条命令 shell (sh) 或 KornShell (ksh) 设置LD_LIBRARY_PATH环境变量正确:LD_LIBRARY_PATH=. export LD_LIBRARY_PATH中的等效命令 C shell(csh 或 tcsh)如下:
setenv LD_LIBRARY_PATH .如果您在 Windows 95 或 Windows NT 机器,确保
HelloWorld.dll在当前 目录,或在 PATH 环境中列出的目录中 变量。在 Java 2 SDK 1.2 版本中,您还可以指定原生库 java命令行中的路径作为系统属性如下:
java -Djava.library.path=. HelloWorld“
-D”命令行选项 设置 Java 平台系统属性。设置java.library.path"." 的属性指示 Java 虚拟机搜索 当前目录中的本地库。
【讨论】:
Error: Could not find class file for 'YourFileName.class'. stackoverflow.com/questions/19137201/… 时也会派上用场
javac -h . HelloWorld.java。到处都回答了这个问题,但我只是为了 tl;dr. 在这里添加它。
简单来说,只要确保你加载了包含函数定义的相关库,加载遵循 JNI 规范的库并从第一个库中包装目标函数,从你的 Java 类中公开本地方法,你应该是很好。
我建议不要使用原始 JNI,因为它包含大量样板代码,如果你开始包装一个 big C 库,你最终会自责。无论如何,在开始时请随意涉足 JNI,但在实际工作中使用 JNA 之类的东西。
【讨论】:
您的选择包括:
Java 原生接口
见:https://en.wikipedia.org/wiki/Java_Native_Interface
引用:
JNI 使程序员能够编写本地方法来处理无法完全用 Java 编程语言编写应用程序的情况,例如当标准 Java 类库不支持特定于平台的功能或程序库时
Java 原生访问
见:https://en.wikipedia.org/wiki/Java_Native_Access
引用:
Java Native Access 是一个社区开发的库,它使 Java 程序无需使用 Java Native 接口即可轻松访问本机共享库。
JNR-FFI
见:https://github.com/jnr/jnr-ffi
引用:
jnr-ffi 是一个 java 库,用于加载原生库,无需手动编写 JNI 代码,也无需使用 SWIG 等工具。
【讨论】:
在“exotic”类别中,请参阅 NestedVM,它将 C 编译为 Mips,并运行 Mips JVM 中的虚拟机。
【讨论】:
结帐 JNAerator。 https://code.google.com/p/jnaerator/
您需要提供源代码和预处理器定义等。
【讨论】:
如果您使用的是 Windows 和 MinGW gcc,如果您在 lib 中遇到特定方法的 UnsatisfiedLinkError,则可能需要额外的标志:
gcc -D_JNI_IMPLEMENTATION_ -Wl,--kill-at -I"%JAVA_HOME%"\include -I"%JAVA_HOME%"\include\win32 BestCode.c -shared -o BestCode.dll
【讨论】:
@Jonas 给出了一个非常详尽的答案,但我认为它也非常值得检查这个网站,你会在那里得到所有重要的答案:
http://www.ntu.edu.sg/home/ehchua/programming/java/javanativeinterface.html
它解释了如何使用 JNI 调用程序:
【讨论】:
我找到了解决这个问题的方法。您需要确保使用 64 位 c++ 编译器编译代码,以调用在 64 位 JRE 上运行的 java 函数。除此之外,我们还需要将创建的dll文件的路径保存在“环境变量”下的“路径”中。
【讨论】:
首先通过在属性java.library.path设置路径来确保在类路径中加载您的本机库或.dll文件
然后使用System.loadLibrary()
Do not use .dll extension at the end.
【讨论】:
用于制作 64 位兼容的 dll 从下面的语句中删除“-MD”选项
cl -Ic:\java\include -Ic:\java\include\win32 -MD -LD HelloWorld.c -FeHelloWorld.dll
【讨论】: