Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/363.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java调用API:从Java代码中调用C函数_Java_C_Callback_Java Native Interface_Invoke - Fatal编程技术网

Java调用API:从Java代码中调用C函数

Java调用API:从Java代码中调用C函数,java,c,callback,java-native-interface,invoke,Java,C,Callback,Java Native Interface,Invoke,我有一个C(navive)程序和一个带有main()方法的jar文件。从我的本机程序中,我正在初始化JVM,并调用main()方法。我对此没有问题,一切都很好。但后来我想从java代码中调用一个C函数 C函数在中的本机代码中定义,与创建JVM的模块相同。标题是自动生成的,正文如下所示: JNIEXPORT void JNICALL Java_eu_raman_chakhouski_NativeUpdaterBus_connect0(JNIEnv* env, jclass clazz) {

我有一个C(navive)程序和一个带有
main()
方法的jar文件。从我的本机程序中,我正在初始化JVM,并调用
main()
方法。我对此没有问题,一切都很好。但后来我想从java代码中调用一个C函数

C函数在中的本机代码中定义,与创建JVM的模块相同。标题是自动生成的,正文如下所示:

JNIEXPORT void JNICALL Java_eu_raman_chakhouski_NativeUpdaterBus_connect0(JNIEnv* env, jclass clazz)
{
    return;
}
因此,从我调用的java代码
nativeUpdateBus.connect0()
,继续得到一个
不满意的链接错误。我在java代码中没有调用System.loadLibrary()
,因为我认为如果目标模块(可能?)已经加载,那么从java代码中调用本机代码不会有问题

嗯,也许我的方法是完全错误的,但我看不到任何明显的缺陷,也许你能帮忙

可能会有什么帮助(但我没有尝试过这些方法中的任何一种,因为我仍然不太确定)

  • 在这些JNI方法中使用一种“蹦床”动态库,从java代码中加载它,然后通过它封送本机调用
  • 定义
    java.lang.Runnable
    的匿名继承器,使用
    jni\u env->DefineClass()
    创建,但这涉及一些字节码技巧
  • 使用另一种侵入性较小的方法,如套接字、命名管道等。但在我的例子中,我只使用一个本机进程,因此这可能是一种过度使用
我正在使用OpenJDK11.0.3和Windows10。我的C程序是使用Microsoft cl.exe 19.16.27031.1 for x64编译的(Visual Studio 2017)。

系统。loadLibrary()
对于jni查找工作至关重要。您还有一个更灵活的
System.load()
替代方案


确保本机方法实现是用
extern“C”
声明的,并且没有被链接器隐藏。

正如其他人已经提到的,一种可能性是创建一个共享库(.dll),并从本机代码和Java调用它以交换数据

但是,如果您想回调JVM最初创建的模块所在的本机代码中定义的C函数,可以使用RegisterNatives

简单示例

  • C程序创建JVM
  • 它调用一个类的Main
  • Java Main在调用C代码中回调名为connect0的C函数
  • 为了得到一个测试用例,本机C函数构造一个Java字符串并返回它
  • Java端打印结果
Java

package com.software7.test;

public class Main {
    private native String connect0() ;

    public static void main(String[] args) {
        Main m = new Main();
        m.makeTest(args);
    }

    private void makeTest(String[] args) {
        System.out.println("Java: main called");
        for (String arg : args) {
            System.out.println(" -> Java: argument: '" + arg + "'");
        }
        String res = connect0(); //callback into native code
        System.out.println("Java: result of connect0() is '" + res + "'"); //process returned String
    }
}
C程序

可以用C创建JavaVM,如图所示 (不仅适用于cygwin,还适用于VS 2019),然后使用RegisterNative C回调进行注册。因此,使用上面链接中的函数invoke_类可以如下所示:

#include <stdio.h>
#include <windows.h>
#include <jni.h>
#include <stdlib.h>
#include <stdbool.h>

... 

void invoke_class(JNIEnv* env) {
    jclass helloWorldClass;
    jmethodID mainMethod;
    jobjectArray applicationArgs;
    jstring applicationArg0;

    helloWorldClass = (*env)->FindClass(env, "com/software7/test/Main");

    mainMethod = (*env)->GetStaticMethodID(env, helloWorldClass, "main", "([Ljava/lang/String;)V");

    applicationArgs = (*env)->NewObjectArray(env, 1, (*env)->FindClass(env, "java/lang/String"), NULL);
    applicationArg0 = (*env)->NewStringUTF(env, "one argument");
    (*env)->SetObjectArrayElement(env, applicationArgs, 0, applicationArg0);

    (*env)->CallStaticVoidMethod(env, helloWorldClass, mainMethod, applicationArgs);
}

jstring connect0(JNIEnv* env, jobject thiz);

static JNINativeMethod native_methods[] = {
        { "connect0", "()Ljava/lang/String;", (void*)connect0 },
};

jstring connect0(JNIEnv* env, jobject thiz) {
    printf("C: connect0 called\n");
    return (*env)->NewStringUTF(env, "Some Result!!");
}

static bool register_native_methods(JNIEnv* env) {
    jclass clazz = (*env)->FindClass(env, "com/software7/test/Main");
    if (clazz == NULL) {
        return false;
    }
    int num_methods = sizeof(native_methods) / sizeof(native_methods[0]);
    if ((*env)->RegisterNatives(env, clazz, native_methods, num_methods) < 0) {
        return false;
    }
    return true;
}


int main() {
    printf("C: Program starts, creating VM...\n");

    JNIEnv* env = create_vm();
    if (env == NULL) {
        printf("C: creating JVM failed\n");
        return 1;
    }
    if (!register_native_methods(env)) {
        printf("C: registering native methods failed\n");
        return 1;
    }
    invoke_class(env);

    destroy_vm();
    getchar();
    return 0;
}
#包括
#包括
#包括
#包括
#包括
... 
void invoke_类(JNIEnv*env){
jclass helloWorldClass;
jmethode方法;
jobjectArray应用程序args;
jstring应用程序arg0;
helloWorldClass=(*env)->FindClass(env,“com/software7/test/Main”);
mainMethod=(*env)->GetStaticMethodID(env,helloWorldClass,“main”,“([Ljava/lang/String;)V”);
applicationArgs=(*env)->NewObjectArray(env,1,(*env)->FindClass(env,“java/lang/String”),NULL;
applicationArg0=(*env)->NewStringUTF(env,“一个参数”);
(*env)->SetObjectArrayElement(env,applicationArgs,0,applicationArg0);
(*env)->CallStaticVoidMethod(env、helloWorldClass、mainMethod、applicationArgs);
}
jstring connect0(JNIEnv*env,jobject thiz);
静态JniationMethod本机_方法[]={
{“connect0”,“()Ljava/lang/String;”,(void*)connect0},
};
jstring connect0(JNIEnv*env,jobject thiz){
printf(“C:connect0调用\n”);
return(*env)->NewStringUTF(env,“Some Result!!”);
}
静态布尔寄存器本地方法(JNIEnv*env){
jclass clazz=(*env)->FindClass(env,“com/software7/test/Main”);
if(clazz==NULL){
返回false;
}
int num_methods=sizeof(native_methods)/sizeof(native_methods[0]);
if((*env)->注册表项(env、clazz、native_方法、num_方法)<0){
返回false;
}
返回true;
}
int main(){
printf(“C:程序启动,创建VM…\n”);
JNIEnv*env=create_vm();
if(env==NULL){
printf(“C:创建JVM失败\n”);
返回1;
}
if(!register_native_methods(env)){
printf(“C:注册本机方法失败\n”);
返回1;
}
调用_类(env);
销毁_vm();
getchar();
返回0;
}
结果

链接

从C程序创建JVM:


注册本机方法:

我没有这样做,但似乎可以对调用JVM的可执行文件中的函数执行回调。但我希望您需要正确构建应用程序以支持该操作。否则,所需函数很可能无法动态链接。T阻力最小的方法可能是将函数移到DSO。如果主程序链接DSO,则您可能不必使用
System.loadLibrary()
。非常感谢,这正是我要找的;您的答案已被接受。