JNI CallStaticObjectMethod 导致 Java 21 崩溃

问题描述 投票:0回答:1

我工作的公司最近从 Java 8 迁移到 Java 21,它还更新了 JNI 版本以及与 C++ 的互操作性。通过 JNI 调用的所有函数(用 C++ 编写)在 Java 8 上都可以正常工作,但是 函数

CallStaticObjectMethod
抛出访问冲突异常并导致崩溃。

该函数用于调用Java端的静态方法。

调用

CallStaticObjectMethod
的函数如下:

jobject Bindings::ToSystemArchObj(JNIEnv* env, const std::string& arch)
{
    if (env == nullptr || arch == ARCH_UNSUPPORTED)
    {
        return nullptr;
    }
    jobject jstr = env->NewStringUTF(arch.c_str()); // I have tried converting the type of jstr from jobject to jstring

    processArchObj = env->CallStaticObjectMethod(JniProcessInfoDTO::SystemArchClass, JniProcessInfoDTO::ofMethodId, jstr); // THIS LINE IS THROWING access violation and causing crash


    return processArchObj;
}

此函数的

JniProcessInfoDTO::SystemArchClass
JniProcessInfoDTO::ofMethodId
参数正在下面的单独函数中初始化:

void JniProcessInfoDTO::initClass(JNIEnv* env)
{
    jclass const localProcessInfoDTO = env->FindClass(JavaTypes::ProcessInfoDTO.c_str());
    ProcessInfoDTORef = reinterpret_cast<jclass>(env->NewGlobalRef(localProcessInfoDTO));
    initFieldIDs(env);
    // std::string const SystemArch = "Lcom/company/mfw/model/dto/SystemArch;";
    SystemArchClass = env->FindClass(JavaTypes::SystemArch.c_str());
    
    // (Ljava/lang/String;)Lcom/company/mfw/model/dto/SystemArch;
    const std::string ofSign = "(" + JavaTypes::String + ")" + JavaTypes::SystemArch; //// std::string const String = "java/lang/String;";

    ofMethodId = env->GetStaticMethodID(SystemArchClass, "of", ofSign.c_str());
}

应该调用的 Java 端静态函数

do
如下所示:

package com.company.mfw.model.dto;

public enum SystemArch {

    X86, X86_64, UNSUPPORTED;

    public static SystemArch of(String arch) {
        try {
            return SystemArch.valueOf(arch);
        } catch (Exception ignored) {
            return UNSUPPORTED;
        }
    }

}

JNI 的加载和卸载方式如下:

extern "C"
{
    // cppcheck-suppress unusedFunction
    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*)
    {
        // This is an entry point for initialization of Java classes.
        // All required Java classes must exist and must be initialized here.
        // JNI_OnLoad must not be wrapped with Safe Environment.
        // JVM should be crashed in case of exceptions here.
        

        JniGlobals::JVMConnector::InitJavaVM(vm);

        JavaVMAttachArgs args;
        args.version = JNI_VERSION_21;
        args.name = nullptr;
        args.group = nullptr;
        JNIEnv* env = nullptr;

        vm->AttachCurrentThread((void**)&env, &args);
        JniContext::Current().JniEnv = env;
        JniGlobals::unmaskFPSR();

        GdiplusStartup(&JniGlobals::gdiplusToken, &JniGlobals::gdiplusStartupInput, nullptr);

        Bindings::JniComponentDTO::initClass(env);
        Bindings::JniWindowDTO::initClass(env);
        Bindings::JniPointDTO::initClass(env);
        Bindings::JniDimensionDTO::initClass(env);
        Bindings::JniRectangleDTO::initClass(env);
        Bindings::JniProcessInfoDTO::initClass(env); 
        Bindings::JniProcessStartupInfoDTO::initClass(env);
        Bindings::JniProcessEndInfoDTO::initClass(env);
        Bindings::JniKeyDTO::initClass(env);
        Bindings::JniNativeMouseEvent::initClass(env);
        Bindings::JniNativeKeyEvent::initClass(env);
        Bindings::JniHooksSettings::initClass(env);
        Bindings::WinSessionDTO::initClass(env);

        JniGlobals::TimeManager::GetInstance().InitJavaHandlers(env);

        JniGlobals::JavaObjectFactory::JavaObjectFactoryClass = env->FindClass("com/company/mfw/bridge/internal/JavaObjectsFactory");
        JniGlobals::JavaObjectFactory::NewStringMethodID
            = env->GetStaticMethodID(JniGlobals::JavaObjectFactory::JavaObjectFactoryClass, "newString", "([B)Ljava/lang/String;");

        Log::GetInstance().Init();

        LOG_TRACE(L"BEGIN");
        auto osInfo = JniGlobals::WindowsSystemInfo::GetInstance().ReadOsInfo();
        std::string csd(osInfo.szCSDVersion);
        LOG_INFO(L"System info. Version: {}.{}, Build: {}, Platform ID: {}, CSD Version: {}", (int)osInfo.dwMajorVersion,
                 (int)osInfo.dwMinorVersion, (int)osInfo.dwBuildNumber, (int)osInfo.dwPlatformId,
                 csd.length() > 0 ? StringsConverter::StringToWstring(csd) : L"<empty>");

        return JNI_VERSION_21;
    }

    // cppcheck-suppress unusedFunction
    JNIEXPORT void JNICALL JNI_OnUnload(JavaVM*, void*)
    {
        LOG_TRACE(L"BEGIN");

        Gdiplus::GdiplusShutdown(JniGlobals::gdiplusToken);

        JNIEnv* env = JniContext::Current().JniEnv;
        Bindings::JniComponentDTO::releaseClass(env);
        Bindings::JniWindowDTO::releaseClass(env);
        Bindings::JniPointDTO::releaseClass(env);
        Bindings::JniDimensionDTO::releaseClass(env);
        Bindings::JniRectangleDTO::releaseClass(env);
        Bindings::JniProcessInfoDTO::releaseClass(env);
        Bindings::JniProcessStartupInfoDTO::releaseClass(env);
        Bindings::JniProcessEndInfoDTO::releaseClass(env);
        Bindings::JniKeyDTO::releaseClass(env);
        Bindings::JniNativeMouseEvent::releaseClass(env);
        Bindings::JniNativeKeyEvent::releaseClass(env);
        Bindings::JniHooksSettings::releaseClass(env);
        Bindings::WinSessionDTO::releaseClass(env);
    }

} // extern "C"

JNI 文档不包含有关此函数的任何信息。有人可以帮我吗?

更新 1:所有其他 JNI 方法都工作正常,只有这个方法导致崩溃

更新2 所有这些函数都被调用以将C++对象类型转换为Java类型。

Bindings::ToSystemArchObj
方法正在被执行此类型转换的方法调用:

/**
 * Converts a ProcessInfoPtr to a Java ProcessInfoDTO object.
 *
 * @param env The JNI environment pointer.
 * @param pi The ProcessInfoPtr object.
 * @return The corresponding Java ProcessInfoDTO object or nullptr if the environment or ProcessInfoPtr is null.
 */
jobject Bindings::ToJavaType(JNIEnv* env, ProcessInfoPtr pi)
{
    if (env == nullptr || pi == nullptr)
    {
        return nullptr;  // Return nullptr if environment or ProcessInfoPtr is null
    }

    jobject joProcessInfoDTO = env->AllocObject(JniProcessInfoDTO::ProcessInfoDTORef);  // Allocate a new ProcessInfoDTO object

    env->SetIntField(joProcessInfoDTO, JniProcessInfoDTO::processPidFieldId, pi->pid);  // Set process PID field
    env->SetIntField(joProcessInfoDTO, JniProcessInfoDTO::processParentPidFieldId, pi->parentPid);  // Set process parent PID field

    env->SetObjectField(joProcessInfoDTO, JniProcessInfoDTO::processChildProcessFieldId, ToJavaType(env, pi->childProcesses));  // Set child processes field

    env->SetObjectField(joProcessInfoDTO, JniProcessInfoDTO::processCommandLineFieldId, ToJavaType(env, pi->commandLine));  // Set command line field
    env->SetObjectField(joProcessInfoDTO, JniProcessInfoDTO::processNameFieldId, ToJavaType(env, pi->name));  // Set process name field
    env->SetObjectField(joProcessInfoDTO, JniProcessInfoDTO::processOwnerFieldId, ToJavaType(env, pi->owner));  // Set process owner field

    env->SetIntField(joProcessInfoDTO, JniProcessInfoDTO::processResidentBytesFieldId, pi->residentBytes);  // Set resident bytes field
    env->SetIntField(joProcessInfoDTO, JniProcessInfoDTO::processTotalBytesFieldId, pi->totalBytes);  // Set total bytes field
    env->SetIntField(joProcessInfoDTO, JniProcessInfoDTO::processSystemMillsFieldId, pi->systemMillis);  // Set system milliseconds field
    env->SetIntField(joProcessInfoDTO, JniProcessInfoDTO::processUserMillsFieldId, pi->userMillis);  // Set user milliseconds field

    env->SetObjectField(joProcessInfoDTO, JniProcessInfoDTO::processArchFieldId, ToSystemArchObj(env, pi->arch));  // Set process architecture field

    return joProcessInfoDTO;  // Return the ProcessInfoDTO object
}

上面的函数尝试将

ProcessInfo
转换为 Java
ProcessInfoDTO
对象。 ProcessInfo类型如下。

class ProcessInfo
{
public:
    ProcessInfo()
        : pid(-1), parentPid(0), commandLine(L""), name(L""), owner(L""), residentBytes(0), totalBytes(0), systemMillis(0), userMillis(0),
          arch("")
    {
        childProcesses.resize(0);
    };

    ~ProcessInfo() = default;

public:
    int pid;
    int parentPid;
    std::vector<int> childProcesses;
    std::wstring commandLine;
    std::wstring name;
    std::wstring owner;

    long residentBytes;
    long totalBytes;
    long systemMillis;
    long userMillis;

    std::string arch;
};

部分

ProcessInfoDTO
类型如下:

public class ProcessInfoDTO {

    private static final long serialVersionUID = -212324578578245L;

    private int pid;
    private int parentPid;
    private String commandLine;
    private String name;
    private String owner;
    private int[] childProcesses;

    private long residentBytes;
    private long totalBytes;
    private long systemMillis;
    private long userMillis;

    private SystemArch arch;

    public int getPid() {
        return pid;
    }

    public void setPid(int pid) {
        this.pid = pid;
    }
    // Some more function definitions
    // Some more function definitions
}
java c++ c++17 java-native-interface java-21
1个回答
0
投票

问题是因为使用了 C 风格的 vararg 函数。

    jvalue args[1];
    args[0].l = jstr;

    jobject processArchObj = env->CallStaticObjectMethodA(JniProcessInfoDTO::SystemArchClass, JniProcessInfoDTO::ofMethodId, args);
© www.soinside.com 2019 - 2024. All rights reserved.