Java调用API:从Java代码中调用C函数
我有一个C(navive)程序和一个带有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) {
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()
- 使用另一种侵入性较小的方法,如套接字、命名管道等。但在我的例子中,我只使用一个本机进程,因此这可能是一种过度使用
系统。loadLibrary()
对于jni查找工作至关重要。您还有一个更灵活的System.load()
替代方案
确保本机方法实现是用
extern“C”
声明的,并且没有被链接器隐藏。正如其他人已经提到的,一种可能性是创建一个共享库(.dll),并从本机代码和Java调用它以交换数据
但是,如果您想回调JVM最初创建的模块所在的本机代码中定义的C函数,可以使用RegisterNatives
简单示例
- C程序创建JVM
- 它调用一个类的Main
- Java Main在调用C代码中回调名为connect0的C函数
- 为了得到一个测试用例,本机C函数构造一个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()
。非常感谢,这正是我要找的;您的答案已被接受。