One way to make JNI work is to go into Environment Variables
and add into User
or System
Path
the folder where jvm.dll
is located (.../bin/server
). But this means that on a client machine, when your app is launched, you need to check if the path is added, and if it isn't, to add it.
But instead of doing this, can you give the location to JavaVMOption
, or using another way, but inside the code and not having to add that location to Environment Variables
?
I'm using Eclipse Adoptium
which will be added with the app files with a license agreement, so the location will be dependent to where the app is placed/installed, making the option with Environment Variables
a bit annoying as the location could change, so you need to remove the old one and add the new one each time in this situation.
This is what I use to create the JVM
, where javaLocation
is std::string
, and it is given as a parameter for the function which contains the location of the app folder:
javaLocation.insert(0, "-Djava.class.path=");
javaLocation.append("Data\\Java");
JavaVMInitArgs vm_args;
JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = &javaLocation[0];
vm_args.version = JNI_VERSION_10;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = false;
JNIEnv* env = nullptr;
jint rc = JNI_OK;
if (jvm == nullptr) {
rc = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
}
else {
rc = jvm->AttachCurrentThread((void**)&env, NULL);
}
delete[] options;
if (rc != JNI_OK) {
if (rc == JNI_EVERSION)
return "JNI_EVERSION";
else if (rc == JNI_ENOMEM)
return "JNI_ENOMEM";
else if (rc == JNI_EINVAL)
return "JNI_EINVAL";
else if (rc == JNI_EEXIST)
return "JNI_EEXIST";
else
return "JNI_FATALERROR";
}
return "JNI_CREATED";
This question was asked before in this thread and was marked as "This question already has answers here", but unfortunately the answer given there doesn't apply to JNI, and it is a general solution for DLL files.
Using the suggestion @Botje gave (thank you!), I tried yet again using LoadLibraryA
, and this time I managed to make it work:
#include <string>
#include <iostream>
#include <chrono>
#include <thread>
#include <jni.h>
#include <Windows.h>
JavaVM* jvm = nullptr;
std::string createVM(std::string location) {
std::string jvmLocation = location;
jvmLocation.append("ThirdParty\\Eclipse Adoptium\\jre-17.0.7.7-hotspot\\bin\\servers\\jvm.dll");
HMODULE hJVMDLL = LoadLibraryA(jvmLocation.c_str());
typedef jint(JNICALL* fpCJV)(JavaVM**, void**, void*);
if (hJVMDLL != NULL) {
fpCJV JNI_CreateJavaVM = (fpCJV)::GetProcAddress(hJVMDLL, "JNI_CreateJavaVM");
location.insert(0, "-Djava.class.path=");
location.append("Data\\Java");
JavaVMInitArgs vm_args;
JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = &location[0];
vm_args.version = JNI_VERSION_10;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = false;
JNIEnv* env = nullptr;
jint rc = JNI_OK;
if (jvm == nullptr) {
rc = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
}
else {
rc = jvm->AttachCurrentThread((void**)&env, NULL);
}
delete[] options;
if (rc != JNI_OK) {
if (rc == JNI_EVERSION)
return "JNI_EVERSION";
else if (rc == JNI_ENOMEM)
return "JNI_ENOMEM";
else if (rc == JNI_EINVAL)
return "JNI_EINVAL";
else if (rc == JNI_EEXIST)
return "JNI_EEXIST";
else
return "JNI_FATALERROR";
}
return "JNI_CREATED";
}
else {
return "ERROR_LOADING_DLL";
}
}
//This is just a test for a function I have written in Java, which doesn't take any parameters, and returns a String
std::string createIdentification() {
JNIEnv* env;
jvm->AttachCurrentThread((void**)&env, NULL);
jclass jClass = env->FindClass("JavaMethods");
if (jClass == nullptr) {
return "ClassNotFound cI";
}
else {
jmethodID methodID = env->GetStaticMethodID(jClass, "createIdentification", "()Ljava/lang/String;");
if (methodID == nullptr) {
return "MethodNotFound cI";
}
else {
jboolean isCopy;
jstring jResult = (jstring)env->CallStaticObjectMethod(jClass, methodID);
const char* string = env->GetStringUTFChars(jResult, &isCopy);
std::string result = string;
env->ReleaseStringUTFChars(jResult, string);
return result;
}
}
}
int main() {
std::cout << createVM("D:\\Program Files\\MyApp\\").c_str() << std::endl;
if (jvm != NULL) {
std::cout << createIdentification().c_str();
}
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
return 0;
}