【问题标题】:Calling dll implemented JNI from C++从 C++ 调用 dll 实现的 JNI
【发布时间】:2019-01-15 04:37:56
【问题描述】:

情况: 我有一个实现 JNI 的 dll,我想从 Cpp 应用程序调用其中的方法。

当前状态: 据我了解,一个实现了JNI的dll其实和JAVA无关,例如: 在 Test.java 中,我写了
public native int Add(int a,int b);
并用 Cpp In TestDll.Cpp 实现
JNIEXPORT jint JNICALL Java_SomeNamespace_Add(JNIEnv* _Env, jobject _Object, jint a, jint b) { return a+b; }
我认为这样的程序与JVM无关,jint结构似乎已经完全定义在jni.h中。
所以,我想知道是否可以直接调用Java_SomeNamespace_Add 而无需从 Cpp 应用程序创建 VM,如果可以的话:

  1. 参数列表中的`JNIEnv*`和`jobject`应该是什么?
  2. 如何将 `jint` 变量转换为标准 `int` 变量?
  3. 如何在不使用 `_Env->FindClass("Ljava/lang/String;")` 和一堆以下代码的情况下将 `jstring` 变量转换为标准 `string` 变量?

【问题讨论】:

  • 1.他们将是空的。 2. jint 和 int32_t 是一回事。 3. jstring不能在没有Java JNI代码的情况下转换为std::string..你可以使用JNI创建一个JVM然后进行转换..
  • @Brandon 我可以只包含 jni.h 并做一些事情然后它神奇地起作用吗?
  • 您是否确认您的实际 DLL 没有对 env 指针和/或接收器对象或可能的参数对象做任何事情?我认为你不能从你的测试函数中概括这一点。

标签: java c++ java-native-interface jnienv


【解决方案1】:

一般来说,有一个原因会阻止您在没有 Java 的情况下加载 JNI DLL。 DLL 可能需要一些 JVM 外部符号。但是,如果需要,您可以用假人来伪造它们。

如果 DLL 是从 Java 为您加载的,那就更容易了:您可以轻松调用 DLL 的任何导出函数。

如果导出的 JNI 方法调用不使用 JNIEnv 参数(如您的 Add() 示例),您可以简单地传递 nullptr 以满足调用约定。

不过,这不会让您传递 jstringjarray 之类的参数。无论如何,好的做法是将 JNI 层和实际逻辑分开,后者将参数从 Java 转换为原生。

但是,如果您无法控制 DLL,则可以尝试完全伪造 JNI。请参阅 jni.h 并以适合您的方式实现必要的功能。

【讨论】:

    【解决方案2】:

    对于第 1 点,我认为使用一些重构创建一个简单的替代方案是个好主意。

    不要将算法的逻辑放入 JNI 调用本身,而是将逻辑移至单独的 c++ 函数并从两个用例中调用该函数。

    即:

    // MyJNIWrapper.cpp
    #include "MyMathFunctions.h"
    JNIEXPORT jint JNICALL Java_SomeNamespace_Add(JNIEnv* _Env, jobject _Object, jint a, jint b)
    {
       return (jint)add((int)a, (int)b);
    }
    
    // MyMathFunctions.h
    int add(int a, int b)
    {
        return a + b;
    }
    
    // MyCppProgram.cpp
    #include "MyMathFunctions.h"
    int main()
    {
        int c = add(a, b)
    }
    

    对于数字 2,您可以使用标准 C 样式转换在 jint 和 int 之间进行转换:

    int a = 10;
    jint b = (jint) a;
    int c = (int) b
    

    至于3号:

    如果您从 JNI 中调用该函数,您将使用 here 中描述的一对函数:

    JNIEXPORT void JNICALLJava_MyJavaClass_printName(JNIEnv *env, jobject obj, jstring name)
    {
        const char *str= (*env)->GetStringUTFChars(env,name,0);
        printf(“%s”, str);
        //need to release this string when done with it in order to
        //avoid memory leak
        (*env)->ReleaseStringUTFChars(env, name, str);
    }
    

    要将上面的 C 字符串转换为 std::string,您可以执行以下操作:

    std::string cpp_string = str;
    

    如果您不使用 JNI 调用该函数,您似乎需要尝试滚动您自己的转换。我会看看你是否可以使用现有的 VM 实现,弄清楚它们是如何做到的并模仿它。

    例如,您可以尝试使用以下作为起点的 Android VM: https://android.googlesource.com/platform/dalvik/+/donut-release/vm/Jni.c#2230 https://android.googlesource.com/platform/dalvik.git/+/android-4.3_r3/vm/UtfString.cpp#284

    【讨论】:

    • 如果我无法控制那个 dll 怎么办?
    • 我会尝试创建一个 JVM 实例。您可以在此处查看如何执行此操作:stackoverflow.com/a/992876/1345115 这将创建一个 JNIEnv 供您作为第一个参数传递。我怀疑在大多数情况下您可以将第二个参数 obj 保留为 NULL。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-09
    • 1970-01-01
    相关资源
    最近更新 更多