65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.47/5 (32投票s)

2008 年 1 月 13 日

CPOL

3分钟阅读

viewsIcon

450459

downloadIcon

7998

本文介绍了如何使用 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 函数,你需要执行以下操作

  1. 使用 FindClass(,,) 方法获取类引用。
  2. 使用 GetStaticMethodIDGetMethodID 函数调用,获取要调用的类的函数的 方法 ID。
  3. 使用 CallStaticVoidMethodCallStaticIntMethodCallStaticObjectMethod 调用函数。

这里要注意的一点是,在获取方法 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>

必备组件

在踏上艰难的道路之前,了解基本概念并安装各种框架和工具非常重要。

  1. 你需要 Sun Java Developer Kit (JDK)。 我推荐 Java 1.6.0
  2. 安装的任何 C/C++ 编译器。

如何运行

要使用此代码,请按照以下说明操作

  • 使用 javac 命令编译 *.java 文件。
  • 使用任何 C++ 编译器编译 CTest.cpp 文件; 我使用了 MSVC++ 6。

将此代码从 C++ 转换为纯 C

附带的代码是用 C++ 编写的。 要将此代码转换为纯 C,你需要修改 CTest.cpp 文件中的以下内容

用途

(*env)->FunctionName(env,args,..);

而不是

env->FunctionName(args,..);
© . All rights reserved.