我工作的公司最近从 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
}
问题是因为使用了 C 风格的 vararg 函数。
jvalue args[1];
args[0].l = jstr;
jobject processArchObj = env->CallStaticObjectMethodA(JniProcessInfoDTO::SystemArchClass, JniProcessInfoDTO::ofMethodId, args);