How to use JNI without setting Environment Variables
-
I found the following code posted by @Richard-MacCutchan to a thread about how to use JNI without having to add the location of jvm.dll into Environment Variables. I kinda understood how it works but still...
/*
******************************************************************************
*
* Name : cpptojava.cpp
*
* Function : Basic Console application to call a Java class.
*
* Created : 09 Jun 2022
*
******************************************************************************
*/#include
#include
#include
#include
#include
#include // requires -std:c++17#include "jni.h"
//
// Find the path to the jvm.dll given the base path to the Java runtime (or JDK if installed)
//
HMODULE LoadDll(
const char* jrePath
)
{
const char* pszjvmname = "bin\\server\\jvm.dll"; // Java VM name
HMODULE hJVMDLL = 0;std::filesystem::path jvmpath(jrePath); jvmpath /= pszjvmname; if (!std::filesystem::exists(jvmpath)) { std::cerr << "JVM library " << jvmpath << " not found." << std::endl; // throw an exception } // load the Java VM DLL into our address space // NB filesystem::path is Unicode hJVMDLL = LoadLibraryW(reinterpret\_cast(jvmpath.c\_str())); if (hJVMDLL != NULL) { std::clog << "jvm.dll loaded from: " << jvmpath << std::endl; } return hJVMDLL; // a Windows HANDLE to the DLL
}
//
// A function to load the Java VM and initialise the JNI interface
// There are a number of diagnostic messages to show progress
//
JNIEnv* AttachJVM(
const char* jrePath,
JavaVM* jvm,
std::string strcwd
)
{
HMODULE hJVMDLL = LoadDll(jrePath);
if (hJVMDLL == 0)
{
return nullptr;
}
typedef jint(JNICALL* fpCJV)(JavaVM**, void**, void*);
fpCJV JNI_CreateJavaVM = (fpCJV)::GetProcAddress(hJVMDLL, "JNI_CreateJavaVM");// tell the JVM where to find the class JavaVMOption options\[2\]; std::stringstream ssoptions; ssoptions << "-Djava.class.path="; ssoptions << strcwd; std::string stropts = ssoptions.str(); options\[0\].optionString = const\_cast(stropts.c\_str()); options\[1\].optionString = const\_cast("-verbose:jni"); // print JNI-related messages JavaVMInitArgs vm\_args; // JDK/JRE 6 VM initialization
-
I found the following code posted by @Richard-MacCutchan to a thread about how to use JNI without having to add the location of jvm.dll into Environment Variables. I kinda understood how it works but still...
/*
******************************************************************************
*
* Name : cpptojava.cpp
*
* Function : Basic Console application to call a Java class.
*
* Created : 09 Jun 2022
*
******************************************************************************
*/#include
#include
#include
#include
#include
#include // requires -std:c++17#include "jni.h"
//
// Find the path to the jvm.dll given the base path to the Java runtime (or JDK if installed)
//
HMODULE LoadDll(
const char* jrePath
)
{
const char* pszjvmname = "bin\\server\\jvm.dll"; // Java VM name
HMODULE hJVMDLL = 0;std::filesystem::path jvmpath(jrePath); jvmpath /= pszjvmname; if (!std::filesystem::exists(jvmpath)) { std::cerr << "JVM library " << jvmpath << " not found." << std::endl; // throw an exception } // load the Java VM DLL into our address space // NB filesystem::path is Unicode hJVMDLL = LoadLibraryW(reinterpret\_cast(jvmpath.c\_str())); if (hJVMDLL != NULL) { std::clog << "jvm.dll loaded from: " << jvmpath << std::endl; } return hJVMDLL; // a Windows HANDLE to the DLL
}
//
// A function to load the Java VM and initialise the JNI interface
// There are a number of diagnostic messages to show progress
//
JNIEnv* AttachJVM(
const char* jrePath,
JavaVM* jvm,
std::string strcwd
)
{
HMODULE hJVMDLL = LoadDll(jrePath);
if (hJVMDLL == 0)
{
return nullptr;
}
typedef jint(JNICALL* fpCJV)(JavaVM**, void**, void*);
fpCJV JNI_CreateJavaVM = (fpCJV)::GetProcAddress(hJVMDLL, "JNI_CreateJavaVM");// tell the JVM where to find the class JavaVMOption options\[2\]; std::stringstream ssoptions; ssoptions << "-Djava.class.path="; ssoptions << strcwd; std::string stropts = ssoptions.str(); options\[0\].optionString = const\_cast(stropts.c\_str()); options\[1\].optionString = const\_cast("-verbose:jni"); // print JNI-related messages JavaVMInitArgs vm\_args; // JDK/JRE 6 VM initialization
I managed to make it work:
#include
#include
#include
#include
#include
#includeJavaVM* 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";
}
}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("Location_To_App_Folder").c_str() << std::endl;
if (jvm != NULL) {
std::cout << createIdentification().c_str();
}std::this\_thread::sleep\_for(std::chrono::milliseconds(2000)); return 0;
}
Thank you @Richard-MacCutchan for the or
-
I found the following code posted by @Richard-MacCutchan to a thread about how to use JNI without having to add the location of jvm.dll into Environment Variables. I kinda understood how it works but still...
/*
******************************************************************************
*
* Name : cpptojava.cpp
*
* Function : Basic Console application to call a Java class.
*
* Created : 09 Jun 2022
*
******************************************************************************
*/#include
#include
#include
#include
#include
#include // requires -std:c++17#include "jni.h"
//
// Find the path to the jvm.dll given the base path to the Java runtime (or JDK if installed)
//
HMODULE LoadDll(
const char* jrePath
)
{
const char* pszjvmname = "bin\\server\\jvm.dll"; // Java VM name
HMODULE hJVMDLL = 0;std::filesystem::path jvmpath(jrePath); jvmpath /= pszjvmname; if (!std::filesystem::exists(jvmpath)) { std::cerr << "JVM library " << jvmpath << " not found." << std::endl; // throw an exception } // load the Java VM DLL into our address space // NB filesystem::path is Unicode hJVMDLL = LoadLibraryW(reinterpret\_cast(jvmpath.c\_str())); if (hJVMDLL != NULL) { std::clog << "jvm.dll loaded from: " << jvmpath << std::endl; } return hJVMDLL; // a Windows HANDLE to the DLL
}
//
// A function to load the Java VM and initialise the JNI interface
// There are a number of diagnostic messages to show progress
//
JNIEnv* AttachJVM(
const char* jrePath,
JavaVM* jvm,
std::string strcwd
)
{
HMODULE hJVMDLL = LoadDll(jrePath);
if (hJVMDLL == 0)
{
return nullptr;
}
typedef jint(JNICALL* fpCJV)(JavaVM**, void**, void*);
fpCJV JNI_CreateJavaVM = (fpCJV)::GetProcAddress(hJVMDLL, "JNI_CreateJavaVM");// tell the JVM where to find the class JavaVMOption options\[2\]; std::stringstream ssoptions; ssoptions << "-Djava.class.path="; ssoptions << strcwd; std::string stropts = ssoptions.str(); options\[0\].optionString = const\_cast(stropts.c\_str()); options\[1\].optionString = const\_cast("-verbose:jni"); // print JNI-related messages JavaVMInitArgs vm\_args; // JDK/JRE 6 VM initialization
Valentinor wrote:
I'm not making them install Java
That is oddly phrased since they will need to have java installed somehow. As a suggestion only you might want to work on your error handling if it turns out Java isn't installed. Telling a user that a dll is missing is not going to help them nor you when they ask why it isn't working. You might want to also print where you looked when you can't find it.
-
Valentinor wrote:
I'm not making them install Java
That is oddly phrased since they will need to have java installed somehow. As a suggestion only you might want to work on your error handling if it turns out Java isn't installed. Telling a user that a dll is missing is not going to help them nor you when they ask why it isn't working. You might want to also print where you looked when you can't find it.
jschell wrote:
That is oddly phrased since they will need to have java installed somehow.
Why did you stopped there with the quote? As the next part of the phrase contained the reason why I'm not making the install Java:
Valentinor wrote:
but it comes in the app files with a license agreement, I'm using Eclipse Adoptium (...\MyApp\ThirdParty\Eclipse Adoptium\jre-17.0.7.7-hotspot).
So yeah, they don't need to install java, as it is included with the app.
-
jschell wrote:
That is oddly phrased since they will need to have java installed somehow.
Why did you stopped there with the quote? As the next part of the phrase contained the reason why I'm not making the install Java:
Valentinor wrote:
but it comes in the app files with a license agreement, I'm using Eclipse Adoptium (...\MyApp\ThirdParty\Eclipse Adoptium\jre-17.0.7.7-hotspot).
So yeah, they don't need to install java, as it is included with the app.
Valentinor wrote:
So yeah, they don't need to install java,
On Windows a Java install consists basically of the following. 1. Lay down the files into the file system. 2. Create the registry data so the user can uninstall it. So apparently what you are actually doing is giving the user the option to use the Java that is already installed or to use the VM that comes with your application. Besides my other suggestion you might also want to consider whether the VM they have installed is the correct version for your appliction.
-
Valentinor wrote:
So yeah, they don't need to install java,
On Windows a Java install consists basically of the following. 1. Lay down the files into the file system. 2. Create the registry data so the user can uninstall it. So apparently what you are actually doing is giving the user the option to use the Java that is already installed or to use the VM that comes with your application. Besides my other suggestion you might also want to consider whether the VM they have installed is the correct version for your appliction.
jschell wrote:
1. Lay down the files into the file system.
Yes or no, depending what you mean by "file system". When the app is installed, inside it's folder structure it will also have Eclipse Adoptium (...\MyApp\ThirdParty\Eclipse Adoptium\jre-17.0.7.7-hotspot)
jschell wrote:
2. Create the registry data so the user can uninstall it.
It will be for the app itself and not just for java, as Eclipse Adoptium will be a part of the app, integrated into it, and not separate. When the app is uninstalled, so will Eclipse Adoptium as it is part of its files. If the user has any other form of Java installed, they won't be affected in any way.
jschell wrote:
So apparently what you are actually doing is giving the user the option to use the Java that is already installed or to use the VM that comes with your application.
No. The app will only use Java that comes with it, as the app won't even look in another location if Java is installed, only inside it's file structure.
jschell wrote:
whether the VM they have installed is the correct version for your appliction
I explained in the above statement why this doesn't matter for my case.
jschell wrote:
As a suggestion only you might want to work on your error handling if it turns out Java isn't installed. Telling a user that a dll is missing is not going to help them nor you when they ask why it isn't working.
I never said that I don't have an error handling system already added. If any of Java files are missing or are corrupted, or any of the other app files are missing or are corrupted, an error will appear telling the user to run the check for corrupted files or reinstall the app, accompanied by an error message, that will tell me exactly which file is the problem, if it is missing or if it is corrupted.