So i need to use jni to call java method.
i started with this code :
JNIEnv *env1;
JavaVM** jvm1;
JavaVMInitArgs vm_args1;
JavaVMOption options1[1];
options1[0].optionString = "-Djava.class.path=D:\\Java Src\\TestStruct"; //Path to the java source code
vm_args1.version = JNI_VERSION_1_6; //JDK version. This indicates version 1.6
vm_args1.nOptions = 1;
vm_args1.options = options1;
vm_args1.ignoreUnrecognized = 0;
int reAt = JNI_CreateJavaVM(jvm1, (void**)&env1, &vm_args1);
if(reAt < 0)
Label1->Caption = "failed";
else
Label1->Caption = "Success";
I convert jvm.lib to OMF lib for to use in my builder C++ Application :
COFF2OMF jvm.lib jvm2.lib
I added jvm2.lib to the library path to fixe link error about JNI_CreateJavaVM function.
Now my application compile without error.
But it crash when it call JNI_CreateJavaVM function.
I added the jvm.dll near my .exe
What is the pb in there steps??
How can fixe it?
Thx
The first argument to JNI_CreateJavaVM should be a valid pointer to JavaVM*:
JavaVM* jvm1;
int reAt = JNI_CreateJavaVM(&jvm1, ...);
In your example, jvm1 is uninitialized.
Consult the documentation for details.
Related
I am trying to run a java program using c++ code. I tried below code
#include<jni.h>
#include<stdio.h>
int main(int argc, char** argv) {
JavaVM* vm;
JNIEnv* env;
JavaVMInitArgs vm_args;
vm_args.version = JNI_VERSION_1_2;
vm_args.nOptions = 0;
vm_args.ignoreUnrecognized = 1;
// Construct a VM
jint results = JNI_CreateJavaVM(&vm, (void**)& env, &vm_args);
// Construct a String
jstring jstr = env->NewStringUTF("Hello World");
// First get the class that contains the method you need to call
jclass clazz = env->FindClass("java/lang/String");
// Get the method that you want to call
jmethodID to_lower = env->GetMethodID(clazz, "toLowerCase",
"()Ljava/lang/String;");
// Call the method on the object
jobject result = env->CallObjectMethod(jstr, to_lower);
// Get a C-style string
const char* str = env->GetStringUTFChars((jstring)result, NULL);
printf("%s\n", str);
// Clean up
env->ReleaseStringUTFChars(jstr, str);
// Shutdown the VM.
vm->DestroyJavaVM();
}
I used below command to compile the code
g++ LoadJVM.c -I/c/apps64/Java/jdk-11.0.1/include -I/c/apps64/Java/jdk-11.0.1/include/win32 -L/c/apps64/Java/jdk-11.0.1/lib/ -ljvm
It compiles fine, but when i run the executable like below, i am facing error
./a.exe
Error
error while loading shared libraries: jvm.dll: cannot open shared object file: No such file or directory
Any Idea why this jvm.dll is not getting loaded?
PS: I am compiling and running from Git-Bash on windows 10.
It looks like your jvm.dll can not be found.
Let's say we have 32bit MinGW installation (this is the version I have).
simple.cc
#include<jni.h>
#include<stdio.h>
int main(int argc, char** argv) {
JavaVM* vm;
JNIEnv* env;
JavaVMInitArgs vm_args;
vm_args.version = JNI_VERSION_1_2;
vm_args.nOptions = 0;
vm_args.ignoreUnrecognized = 1;
// Construct a VM
jint results = JNI_CreateJavaVM(&vm, (void**)& env, &vm_args);
printf("Hello");
// Shutdown the VM.
(*vm).DestroyJavaVM();
}
compilation and execution
> export JAVA_HOME="/c/Program\ Files\ \(x86\)/Java/jdk1.8.0_211/"
> export PATH="/c/Program Files (x86)/Java/jdk1.8.0_211/jre/bin/server/":"$PATH"
> g++ -o simple simple.cc -I"$JAVA_HOME/include/" -I"$JAVA_HOME/include/win32/" -L"$JAVA_HOME/lib" -ljvm
> ./simple
Hello
you have to make sure that jvm.dll is visible on %PATH% - $PATH inside git-bash.
The following code succeeds most of the time, but sometimes causes the program to crash (all threads exiting with code 1).
string classpath;
JavaVMOption options[6];
std::string heapOptionString = "-Xms500m"; //Important !!! please adjust when needed, default is 1GB of RAM usage = "-Xms1g"
std::string jmxOptionString = "-Dcom.sun.management.jmxremote";
std::string unlockCFOptionString = "-XX:+UnlockCommercialFeatures";
std::string flightRecOptionString = "-XX:+FlightRecorder";
std::string useGC1 = "-XX:+UseG1GC";
options[0].optionString = new char[classpath.size()+1];
strcpy_s(options[0].optionString, classpath.size()+1,classpath.c_str());
options[1].optionString = new char[heapOptionString.size()+1];
strcpy_s(options[1].optionString, heapOptionString.size()+1,heapOptionString.c_str());
options[2].optionString = new char[jmxOptionString.size()+1];
strcpy_s(options[2].optionString, jmxOptionString.size()+1, jmxOptionString.c_str());
options[3].optionString = new char[unlockCFOptionString.size()+1];
strcpy_s(options[3].optionString, unlockCFOptionString.size()+1, unlockCFOptionString.c_str());
options[4].optionString = new char[flightRecOptionString.size()+1];
strcpy_s(options[4].optionString, flightRecOptionString.size()+1, flightRecOptionString.c_str());
options[5].optionString = new char[useGC1.size()+1];
strcpy_s(options[5].optionString, useGC1.size()+1, useGC1.c_str());
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 6;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;
int ret = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
It is inconsistent - sometimes I am able to run my application multiple times during the day, but once the problem appears - it crashes every time and the only way to resolve it is to log off and log in again (some java service hanging in memory?).
I've checked other questions on SO, relating to the creation of VM trough JNI, but they seem to be related to other causes (old bugs in Java 1.6 that are supposed to be fixed now) or have different circumstances - i.e. 100% reproducible crash.
EDIT: I should also mention, that this code is part of a method in a class, that is essentially a wrapper over a JAVA library that I am using. Also it is executed from non-main thread and there is no prior calls to JNI whatsoever.
now I have a question to use "JNI_GetCreatedJavaVMs".
but I couldn't use JNI_OnLoad method because my native code is not provid for java .
void *pHandle = dlopen("/system/lib/libart.so", RTLD_NOW | RTLD_GLOBAL);
JavaVM* m_pJvm = NULL;
void * pFunAddr =dlsym(pHandle, "JNI_GetCreatedJavaVMs");
LOGD("pJNI_GetCreatedJavaVMs = %08X", pFunAddr);
pJNIGetCreatedJavaVMs = (int)pFunAddr - 1;
LOGD("call !!!!!!!");
pJNIGetCreatedJavaVMs(&m_pJvm, 0, &vm_count);
LOGD("pJNIGetCreatedJavaVMs result is %d", result);
when I call the JNI_GetCreatedJavaVMs, process was crashed.
I didn't found what happend in IDA.
who can help me !!!!!!!!!!!!!! THX
ps:JNI_GetCreatedJavaVMs method is found in the android source code.
and another method is use runtime(libart.so) or gdvm(libdvm.so).
Some code from my native app, it is build in the AOSP source tree.
I am not sure if it could work in NDK
#include <jni.h>
#include <android_runtime/AndroidRuntime.h>
...
JNIEnv *env;
jint res;
JavaVM *jvm = AndroidRuntime::getJavaVM();
assert(jvm != NULL);
res = jvm->AttachCurrentThread(&env, NULL);
assert(res >= 0);
I do have a WebApp (WAR file) in a Maven project running fine.
I want to call a Java method in a class file which is inside the maven project from a C file. The C program reads the GPIO input of my beagleboard and when it's on, it runs the Java method. I use JNI for doing that.
I can run a classic HelloWorld Java method, but when I want to run a method from my Maven project, I got a fatal error from my JVM when its trying to load the Java static method.
I added the C code in my WebApp, in /WEB-INF/myapp/classes/classfile/
Class file : App
Method : test
my C program:
JNIEnv* create_vm(JavaVM ** jvm) {
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption options;
//Path to the java source code
options.optionString = "-Djava.class.path=./";
//JDK version. This indicates version 1.6
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 1;
vm_args.options = &options;
vm_args.ignoreUnrecognized = 0;
int ret = JNI_CreateJavaVM(jvm, (void**)&env, &vm_args);
if(ret < 0)
printf("\nUnable to Launch JVM\n");
return env;
}
int main(int argc, char** argv)
{
int fd, ret, data[64];
JNIEnv *env;
JavaVM * jvm;
env = create_vm(&jvm);
if (env == NULL)
return 1;
fd = ret = open(argv[argc - 1], O_RDONLY);
printf("open returned %d\n", ret);
while (1) {
ret = read(fd, &data, sizeof(data));
printf("read returned %d\n", ret);
jclass helloWorldClass = (*env)->FindClass(env, "App");
printf("appel class\n");
jmethodID mainMethod = (*env)->GetStaticMethodID(
env, helloWorldClass, "test", "([Ljava/lang/String;)V");
printf("appel method\n");
(*env)->CallStaticVoidMethod(env, helloWorldClass, mainMethod, NULL);
sleep(2);
}
(*jvm)->DestroyJavaVM(jvm);
exit(0);
}
Java class :
package App;
public class App
{
public static void test (String[] args){
System.out.println("Hello World");
}
}
Have you investigated using Reverse JNI?
I notice that you have the following line:
options.optionString = "-Djava.class.path=./"; //Path to the java source code
The classpath needs to point to either the jar file generated by Maven, or the root directory under which your Java class files are located.
See: Setting the ClassPath,
In a standard Maven project, Maven will create your jar under target/ when you execute mvn package. So you'll likely change -Djava.class.path=./ to -cp /target/App.jar
I want to load my own native libraries in my java application. Those native libraries depend upon third-party libraries (which may or may not be present when my application is installed on the client computer).
Inside my java application, I ask the user to specify the location of dependent libs. Once I have this information, I am using it to update the "LD_LIBRARY_PATH" environment variable using JNI code. The following is the code snippet that I am using to change the "LD_LIBRARY_PATH" environment variable.
Java code
public static final int setEnv(String key, String value) {
if (key == null) {
throw new NullPointerException("key cannot be null");
}
if (value == null) {
throw new NullPointerException("value cannot be null");
}
return nativeSetEnv(key, value);
}
public static final native int nativeSetEnv(String key, String value);
Jni code (C)
JNIEXPORT jint JNICALL Java_Test_nativeSetEnv(JNIEnv *env, jclass cls, jstring key, jstring value) {
const char *nativeKey = NULL;
const char *nativeValue = NULL;
nativeKey = (*env)->GetStringUTFChars(env, key, NULL);
nativeValue = (*env)->GetStringUTFChars(env, value, NULL);
int result = setenv(nativeKey, nativeValue, 1);
return (jint) result;
}
I also have corresponding native methods to fetch the environment variable.
I can successfully update the LD_LIBRARY_PATH (this assertion is based on the output of C routine getenv().
I am still not able to load my native library. The dependent third-party libraries are still not detected.
Any help/pointers are appreciated. I am using Linux 64 bit.
Edit:
I wrote a SSCE (in C) to test if dynamic loader is working. Here is the SSCE
#include
#include
#include
#include
int main(int argc, const char* const argv[]) {
const char* const dependentLibPath = "...:";
const char* const sharedLibrary = "...";
char *newLibPath = NULL;
char *originalLibPath = NULL;
int l1, l2, result;
void* handle = NULL;
originalLibPath = getenv("LD_LIBRARY_PATH");
fprintf(stdout,"\nOriginal library path =%s\n",originalLibPath);
l1 = strlen(originalLibPath);
l2 = strlen(dependentLibPath);
newLibPath = (char *)malloc((l1+l2)*sizeof(char));
strcpy(newLibPath,dependentLibPath);
strcat(newLibPath,originalLibPath);
fprintf(stdout,"\nNew library path =%s\n",newLibPath);
result = setenv("LD_LIBRARY_PATH", newLibPath, 1);
if(result!=0) {
fprintf(stderr,"\nEnvironment could not be updated\n");
exit(1);
}
newLibPath = getenv("LD_LIBRARY_PATH");
fprintf(stdout,"\nNew library path from the env =%s\n",newLibPath);
handle = dlopen(sharedLibrary, RTLD_NOW);
if(handle==NULL) {
fprintf(stderr,"\nCould not load the shared library: %s\n",dlerror());
exit(1);
}
fprintf(stdout,"\n The shared library was successfully loaded.\n");
result = dlclose(handle);
if(result!=0) {
fprintf(stderr,"\nCould not unload the shared library: %s\n",dlerror());
exit(1);
}
return 0;
}
The C code also does not work. Apparently, the dynamic loader is not rereading the LD_LIBRARY_PATH environment variable. I need to figure out how to force the dynamic loader to re-read the LD_LIBRARY_PATH environment variable.
See the accepted answer here:
Changing LD_LIBRARY_PATH at runtime for ctypes
In other words, what you're trying to do isn't possible. You'll need to launch a new process with an updated LD_LIBRARY_PATH (e.g., use ProcessBuilder and update environment() to concatenate the necessary directory)
This is a hack used to manipulate JVM's library path programmatically. NOTE: it relies on internals of ClassLoader implementation so it might not work on all JVMs/versions.
String currentPath = System.getProperty("java.library.path");
System.setProperty( "java.library.path", currentPath + ":/path/to/my/libs" );
// this forces JVM to reload "java.library.path" property
Field fieldSysPath = ClassLoader.class.getDeclaredField( "sys_paths" );
fieldSysPath.setAccessible( true );
fieldSysPath.set( null, null );
This code uses UNIX-style file path separators ('/') and library path separator (':'). For cross-platform way of doing this use System Properties to get system-specific separators: http://download.oracle.com/javase/tutorial/essential/environment/sysprop.html
I have successfully implemented something similar for CollabNet Subversion Edge, which depends on the SIGAR libraries across ALL Operating Systems (we support Windows/Linux/Sparc both 32 bits and 64 bits)...
Subversion Edge is a web application that helps one managing Subversion repositories through a web console and uses SIGAR to the SIGAR libraries helps us provide users data values directly from the OS... You need to update the value of the property "java.library.path" at runtime. (https://ctf.open.collab.net/integration/viewvc/viewvc.cgi/trunk/console/grails-app/services/com/collabnet/svnedge/console/OperatingSystemService.groovy?revision=1890&root=svnedge&system=exsy1005&view=markup Note that the URL is a Groovy code, but I have modified it to a Java here)...
The following example is the implementation in URL above... (On Windows, your user will be required to restart the machine if he/she has downloaded the libraries after or downloaded them using your application)... The "java.library.path" will update the user's path "usr_paths" instead of System path "sys_paths" (permissions exception might be raised depending on the OS when using the latter).
133/**
134 * Updates the java.library.path at run-time.
135 * #param libraryDirPath
136 */
137 public void addDirToJavaLibraryPathAtRuntime(String libraryDirPath)
138 throws Exception {
139 try {
140 Field field = ClassLoader.class.getDeclaredField("usr_paths");
141 field.setAccessible(true);
142 String[] paths = (String[])field.get(null);
143 for (int i = 0; i < paths.length; i++) {
144 if (libraryDirPath.equals(paths[i])) {
145 return;
146 }
147 }
148 String[] tmp = new String[paths.length+1];
149 System.arraycopy(paths,0,tmp,0,paths.length);
150 tmp[paths.length] = libraryDirPath;
151 field.set(null,tmp);
152 String javaLib = "java.library.path";
153 System.setProperty(javaLib, System.getProperty(javaLib) +
154 File.pathSeparator + libraryDirPath);
155
156 } catch (IllegalAccessException e) {
157 throw new IOException("Failed to get permissions to set " +
158 "library path to " + libraryDirPath);
159 } catch (NoSuchFieldException e) {
160 throw new IOException("Failed to get field handle to set " +
161 "library path to " + libraryDirPath);
162 }
163 }
The Bootstrap services (Groovy on Grails application) class of the console runs a service and executes it with the full path to the library directory... UNiX-based servers do not need to restart the server to get the libraries, but Windows boxes do need a server restart after the installation. In your case, you would be calling this as follows:
String appHomePath = "/YOUR/PATH/HERE/TO/YOUR/LIBRARY/DIRECTORY";
String yourLib = new File(appHomePath, "SUBDIRECTORY/").getCanonicalPath();
124 try {
125 addDirToJavaLibraryPathAtRuntime(yourLib);
126 } catch (Exception e) {
127 log.error("Error adding the MY Libraries at " + yourLib + " " +
128 "java.library.path: " + e.message);
129 }
For each OS you ship your application, just make sure to provide a matching version of the libraries for the specific platform (32bit-Linux, 64bit-Windows, etc...).