如何从 C 调用 Java 函数(使用 JNI)






4.47/5 (32投票s)
本文介绍了如何使用 JNI 从 C 语言调用 Java 函数。 还介绍了在 Java 函数中传递/返回简单参数、数组和结构体数组。
引言
本文描述了在 C/C++ 中使用 Java 代码的方法。 我不仅讨论了简单的参数传递和返回,还讨论了诸如结构体和结构体数组等复杂数据结构在 Java 函数中的使用。
背景
几天前,我的经理让我用 C/C++ 编写代码,在其他项目中调用 Java 类。 我花了很长时间才弄清楚如何调用简单函数以及如何传递/返回复杂的数据类型。 我找到了很多文章,描述了使用 JNI 在 Java 中调用 C/C++ 函数,但很少讨论使用 JNI 在 C/C++ 中调用 Java 函数。 那个时候,我决定写一篇文章,以便其他人可以从中获得帮助。
使用代码
CTest.cpp 文件包含从 Java 类调用不同函数的代码。 本项目中使用的 Java 类如下所示
- HelloWorld.java
- ControlDetail.java
- WorkOrder.java
- ReturnData.java
HelloWorld.java 包含将从 CTest.cpp 调用的函数。 其他三个 Java 类只是用于代替 Java 中的结构体。 由于 Java 中没有结构体的概念,我们可以使用类来实现这一点。 这就是另外三个 .java 文件的内容。
HelloWorld.java 包含以下将从 C/C++ 代码调用的函数
public static void main(String args[])
{
}
public static void TestCall(String szArg)
{
}
public static int DisplayStruct(ControlDetail ctrlDetail)
{
}
public static void DisplayStructArray(WorkOrder ArrWO[])
{
}
public static Object ReturnObjFunc()
{
}
要从 C/C++ 调用这些函数,首先你需要使用以下函数加载 JVM
JNIEnv* create_vm(JavaVM ** jvm) {
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption options;
//Path to the java source code
options.optionString = "-Djava.class.path=D:\\Java Src\\TestStruct";
vm_args.version = JNI_VERSION_1_6; //JDK version. This indicates version 1.6
vm_args.nOptions = 1;
vm_args.options = &options;
vm_args.ignoreUnrecognized = 0;
int ret = JNI_CreateJavaVM(jvm, (void**)&env, &vm_args);
if(ret < 0)
printf("\nUnable to Launch JVM\n");
return env;
}
请注意,要使用此代码,您必须修改 options.optionString
变量。 你需要设置 Java 代码的路径:Java 类放置的位置。 目前,它被设置为 D:\Java Src\TestStruct。 你可以根据你的情况修改它。 你还需要在上述代码中修改 JDK 版本信息,如下所示
vm_args.version = JNI_VERSION_1_6; //JDK version. This indicates version 1.6
如果你安装了另一个 JDK 版本,请进行修改。
要从 C 调用特定的 Java 函数,你需要执行以下操作
- 使用
FindClass(,,)
方法获取类引用。 - 使用
GetStaticMethodID
和GetMethodID
函数调用,获取要调用的类的函数的 方法 ID。 - 使用
CallStaticVoidMethod
、CallStaticIntMethod
和CallStaticObjectMethod
调用函数。
这里要注意的一点是,在获取方法 ID 时指定函数签名。
要获取正确的方法签名,你可以使用以下 Java 命令
javap -s -p HelloWorld
它将显示 HelloWorld
类中每个函数的签名。 这些签名可用于获取方法 ID。 上述命令的结果如下所示
D:\Java Src\TestStruct>javap -s -p HelloWorld
Compiled from "HelloWorld.java"
public class HelloWorld extends java.lang.Object{
public HelloWorld();
Signature: ()V
public static void main(java.lang.String[]);
Signature: ([Ljava/lang/String;)V
public static void TestCall(java.lang.String);
Signature: (Ljava/lang/String;)V
public static int DisplayStruct(ControlNEDetail);
Signature: (LControlNEDetail;)I
public static void DisplayStructArray(WorkOrder[]);
Signature: ([LWorkOrder;)V
public static java.lang.Object ReturnObjFunc();
Signature: ()Ljava/lang/Object;
}
请注意,在 GetMethodID
函数中指定方法名称时,如果该方法是构造函数,则其方法名称将为 <init>
。
必备组件
在踏上艰难的道路之前,了解基本概念并安装各种框架和工具非常重要。
- 你需要 Sun Java Developer Kit (JDK)。 我推荐 Java 1.6.0。
- 安装的任何 C/C++ 编译器。
如何运行
要使用此代码,请按照以下说明操作
- 使用 javac 命令编译 *.java 文件。
- 使用任何 C++ 编译器编译 CTest.cpp 文件; 我使用了 MSVC++ 6。
将此代码从 C++ 转换为纯 C
附带的代码是用 C++ 编写的。 要将此代码转换为纯 C,你需要修改 CTest.cpp 文件中的以下内容
用途
(*env)->FunctionName(env,args,..);
而不是
env->FunctionName(args,..);