我在C++中使用JNI和JVMTI,目的是打印目标Java应用程序的所有类名.我遇到的问题是,当试图从每个Java类内部调用getName()方法时,它会崩溃.

    static jvmtiEnv* jvmtiEnv;
    static JNIEnv* Env;

    // The following code is located inside a method
    jint class_count;
    jclass* classes_ptr;
    jvmtiEnv->GetLoadedClasses(&class_count, &classes_ptr);
    Logger::Log("LoadedClassCount: " + std::to_string(class_count));

    for (int i = 0; i < class_count; i++) {
        jclass clazz = classes_ptr[i];

        if (clazz) {
            //Logger::Log("Class Number: " + std::to_string(i));
            jmethodID mid_getName = Env->GetMethodID(clazz, "getName", "()Ljava/lang/String;");
            if (!mid_getName) continue; // This method is not always available.

            Logger::Log("Pass 2");
            jobject classNameObj = Env->CallObjectMethod(clazz, mid_getName); // Crashes here
            if (!classNameObj) continue;
            Logger::Log("Pass 3");

            // Printing fix suggested by @Someprogrammerdude
            std::string str = Env->GetStringUTFChars((jstring) classNameObj, 0);
            Logger::Log(("ClassName: " + str).c_str());  // or Logger::Log("ClassName: " + str)
        }
    }

当然,jvmtiEnv和Env字段已初始化并正常工作,因为我可以调用其他方法,但似乎不知道如何打印类名.以下是我的日志(log)记录方法.我还将日志(log)方法调用打印到主动附加到目标Java应用程序的控制台,但由于代码崩溃,这部分没有用,因此我还需要打印到txt文件.

void Logger::Log(std::string message)
{
    if (!Logger::Initialized) Logger::Init();
    std::cout << "[ LOG ] :: " + message << std::endl;

    std::ofstream log("C:\\Users\\me\\debug.txt", std::ofstream::app | std::ofstream::out);
    log << message << std::endl;
}

推荐答案

为了演示起见,我们假设clazz是对应于java.util.Vector类的Class个实例.用Java的话来说,这个数字应该是Class<Vector>.

因此,这条线

jmethodID mid_getName = Env->GetMethodID(clazz, "getName", "()Ljava/lang/String;");

try 在java.util.Vector中查找getName方法.这失败了,因为事实上,Vector没有getName方法. 即使这不太可能会为给定的类C返回值,您也只能在类C的instances上调用该方法,但您可以在类C本身上调用它.这就是JVM理所当然地崩溃的原因.

相反,您希望查找Class本身的getName方法,或者在Java中查找Class<Class>(以及其他任何地方的"元类"),并在Class<Class>的所有实例上调用该方法,这些实例是classes_ptr中的Class.

有很多方法可以做到这一点,但最简单的方法可能只有以下几个:

jclass cls_Class = Env->FindClass("java/lang/Class");
jmethodID mid_Class_getName = Env->GetMethodID(cls_Class, "getName", "()Ljava/lang/String;");

for (int i = 0; i < class_count; i++) {
   jclass clazz = classes_ptr[i];
   jstring classNameObj = (jstring) Env->CallObjectMethod(clazz, mid_Class_getName);
   ...
}

Java相关问答推荐

收听RDX中用户数据的变化

如何使用CSS为选定但未聚焦的表格行设置背景 colored颜色 ?

转换为Biggram

如何使用Java API在Oracle ODI中运行模拟?

JavaFX如何在MeshView中修复多个立方体?

将带有js文件的 bootstrap 程序导入maven项目时出错

带错误BER验证的itext8签名返回pdf

使用存储在字符串变量中的路径目录打开.pdf文件

无法使用Freemarker从XML中读取重复的标记值

JavaFX:无论何时显示应用程序,如何更改组件/ node 位置?

Spring Validator批注不起作用

Java 21中泛型的不兼容更改

如何从命令行编译包中的所有类?

在打开搜索结果时,如何让Eclipse打开整个文件?

Android无法在Java代码中调用Kotlin代码,原因是在Companion中使用Kotlin枚举时

在WHILE()循环初始化部分中声明和初始化变量的Java语法?

Java递归泛型是否可以被视为继承和重写的语法糖

如何通过gradle命令行从build.gradle获得Java targetCompatibility

将@Transactional添加到Spring框架中链下的每个方法会产生什么效果?

SonarQube在合并升级到java17后对旧代码提出错误