Creating a java7 JVM with the JNI invocation API - c++

We have a class in java 7 and need to load it from native code. I have already used java 6 with JNI but java 6 can't load that class. So I installed the new JDK, changed include directories and link references in my VC project etc. All was well until I wanted to start the jre7 from JNI:
JNI_CreateJavaVM takes the the java version in vm_args.version parameter but there's no definition for a newer version than 1.6.
JavaVMInitArgs vm_args;
...
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 2;
vm_args.options = options;
vm_args.ignoreUnrecognized = 0;
int ret = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
Calling FindClass for the java7 class obviously produces an UnsupportedClassVersionError.
The problem: How do I create a java7 JVM with JNI_CreateJavaVM?

For Mac users, I highly recommend to read this article:
How do I switch between Java 7 and Java 6 on OS X 10.8.2?
https://superuser.com/questions/490425/how-do-i-switch-between-java-7-and-java-6-on-os-x-10-8-2
I successfully compiled and ran the JNI example in which I call some static methods from a Java class I created. I solved the problem by invoking "java_home" like this:
/usr/libexec/java_home -v 1.6.0_45 --exec javac Sample.java

The problem are on path environment variable, that's refers to jre6 before jre7.
On visual C++->Property Pages->Debugging->Environment, I changed to:
path=C:\Java\jre7\bin;C:\Java\jre7\bin\client;%path%
Or directly on: My Computer->Properties->Advanced->Environment Variables

Related

How to fix EXCEPTION_ACCESS_VIOLATION when loading c++ dll file?

The Problem
I am writing a simple Hello World program using the Java Native Interface (JNI). When I call System.loadLibrary("libsimple_JNI") to load the dll file containing the C++ hello world function, an EXCEPTION_ACCESS_VIOLATION (0xc0000005) occurs (I am compiling in Netbeans with 64 bit cygwin).
I have tried statically loading the library as well as loading it within the Java helper function which calls the C++ hello world function. I added the path to the cygwin1.dll in the system environment variables which resolved earlier problems with Java not being able to find the dependencies for my dll file.
Here is the exact error message
A fatal error has been detected by the Java Runtime Environment:
EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x0000000180128dd7, pid=19116, tid=0x0000000000003ea8
JRE version: Java(TM) SE Runtime Environment (8.0_131-b11) (build 1.8.0_131-b11)
Java VM: Java HotSpot(TM) 64-Bit Server VM (25.131-b11 mixed mode windows-amd64 compressed oops)
Problematic frame:
C [cygwin1.dll+0xe8dd7]
Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
An error report file with more information is saved as:
C:\Users\Kogondo Admin\Desktop\JavaApplication7\hs_err_pid19116.log
Here is the Java Code
public static void main(String[] args) {
thingy my_thingy = new thingy();
my_thingy.hi();
}
public class thingy {
public thingy() {}
public void hi(){
System.loadLibrary("libsimple_JNI");
hello();
}
private native void hello();
}
Here is the C++ code
void JNICALL Java_javaapplication7_thingy_hello (JNIEnv * env, jobject object){
printf("hi from C");
}
When running properly, I expect the code to print "hi from C" to the output window in Netbeans.
Final Solution
7 and half hours I finally managed to find a solution. I followed van dench's advice to generate my dll file using the minGW compiler in Visual Studios instead of the cygwin compiler in Netbeans.
Here is a link to the video and the paper that I found which walk through how to do this. Note that the video is in Portuguese. That being said, the paper which it follows is in English and the video walkthrough shows step by step what commands and configurations you need to do.
I am willing to further elaborate on my solution if others would find that useful.
Good Luck!

JNI_CreateJavaVM() terminates with exit code 1

I'm trying to call a Java method from C++ using JNI. To do that I've installed jdk1.7.0_51, linking against jdk1.7.0_51\lib\jvm.lib, including jdk1.7.0_51\include and jdk1.7.0_51\include\win32. using the following code in Visual Studio 2012 I tried to create a Java vm object - but the function always terminates my application with exit code 1 (the function doesn't return 1: my program terminates completly and sends the exit code 1).
#include <iostream>
#include "jni.h"
int main(int argc, char*argv[]){
JNIEnv* env = nullptr;
JavaVM* jvm = nullptr;
JavaVMInitArgs vm_args;
JavaVMOption options[2];
options[0].optionString = "-Djava.class.path=.";
options[1].optionString = "-DXcheck:jni:pedantic";
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 2;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE; // remove unrecognized options
int ret = JNI_CreateJavaVM(&jvm, (void**) &env, &vm_args);
std::cout << "This code is never reached" << std::endl;
return 0;
}
OS: Windows 7 (x64)
Compiler: Visual Studio 2012 (x86/Win32 Project)
Java VM: jdk1.7.0_51, i586 (should be ok in my opinion, because I'm compiling for x86 - otherwise linkage with jvm.lib wouldn't work)
I've already tried to using both: jdk1.7.0_51\jre\bin\client\jvm.dll as well as jdk1.7.0_51\jre\bin\Server\jvm.dll - with the same result (I'm not entirely sure what the difference is though).
Any ideas & suggestions would be highly appreciated.
Using static linking
remove the jvm.dll from your project directories. The dll must be loaded from it's original location, as it seems that other DLLs are involved, found by references.
Set the PATH environement variable to start with the folder of a JRE jvm.dll. And don't use the "c:\folder with space in name" notation (that is surrounding the path with double quotes). Just use set path=c:\folder with space in name;%PATH%. That mistake made my previous attempts worthless.
Using dynamic linking.
remove the jvm.dll from your project directories. The dll must be loaded from it's original location, as it seems that other DLLs are involved, found by references.
Drop jvm.lib from your project configuration
Use LoadLibrary, with the full path for jvm.dll (escape '\' or use '/')
Use GetProcAddress for "JNI_CreateJavaVM"
Make sure to use a proper typedef for the function pointer (use JNICALL as calling convention)
Patching your code with above steps makes my VS2012/Seven64/x86Debug/JDK1.6 project to output "This code is never reached" (with ret == JNI_OK)

Call Java code from C

I use the JNI interface to invoke Java code from C code.
While compiling I use the following command:
gcc -g -I/usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/include/ -I/usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/include/linux/ -L/usr/bin/java -L/usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/amd64/server/ -ljvm calljava.c
And I use the following code to create the JVM:
JNIEnv* create_vm()
{
JavaVM* jvm;
JNIEnv* env;
JavaVMInitArgs args;
JavaVMOption options[1];
args.version = JNI_VERSION_1_2;
args.nOptions = 1;
options[0].optionString = "-Djava.class.path=<classpath>";
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
JNI_CreateJavaVM(&jvm, (void **)&env, &args);
return env;
}
My question is:
Is the path to the JVM hardcoded in the binary?
Can we specify the path to the java executable at runtime?
If there is a way to do that can anyone help me with the compile time flags that can be used for that?
Thanks in advance!
The "java executable" is not used at all. When you compile and link your code, you link against a shared library, the location of which is determined by the system at runtime when you launch your executable.
Unless you dynamically load the jvm shared library yourself from a known location (and subsequently look up and call the functions therein), the system is going to determine the "path to the JVM".
Usually if you want to run against a specific version, you would include that version in your application's distribution, and configure the launch of your application to ensure that the proper shared library is used (either via scripts which set the environment appropriately, dynamically loading it, or other system-specific methods).

JNI, Java to C++ in Eclipse: undefined reference to '_imp__JNI_CreateJavaVM#12'

I'm a beginner to c++ and JNI and I want to call a Java methode from my C++ program.
When compiling (with Eclipse) I get the following error:
undefined reference to '_imp__JNI_CreateJavaVM#12'
I searched for this problem and came across this post
There the answer was, if I get that right, including the jvm library to the compiling command. Since I'm not compiling by hand I'm not sure how I can make Eclipse do this. Could somebody explain that step by step for a complete beginner?
Here is the code, in case the compiling command won't change anything and the code has some errors.
In this part the error is displayed when calling JNI_CreateJavaVM:
JNIEnv* create_vm(JavaVM ** jvm) {
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption options;
/*
* The following is a little messy, but options.optionString wants a char* not a string
* So I convert the String-path to a char-array
*/
string stringPath = "-Djava.class.path=C:\\Users\\Chris\\workspacejava\\PP\\src\\Tests"; //Path to the java source code
int sLen = stringPath.length();
char javaPath [sLen + 1];
int i = 0;
for(; i < sLen; i++)
{
javaPath[i] = stringPath[i];
}
javaPath[i] = '\0';
options.optionString = javaPath;
vm_args.version = JNI_VERSION_1_6; //JDK version. This indicates version 1.6
vm_args.nOptions = 1;
vm_args.options = &options;
vm_args.ignoreUnrecognized = 0;
int ret = JNI_CreateJavaVM(jvm, (void**)&env, &vm_args); //This call results in the error: undefined reference to '_imp__JNI_CreateJavaVM#12'
if(ret < 0)
printf("\nUnable to Launch JVM\n");
return env;}
And here I call this function:
int main(int argc, char* argv[])
{
JNIEnv *env;
JavaVM * jvm;
env = create_vm(&jvm);
if (env == NULL)
return 1;
...
int n = jvm->DestroyJavaVM();
return 0;
}
Further Informations: (I don't know if they help)
I use Windows 7. Both Eclipse and JDK are 64Bit. I'm using MinGW GCC to compile my code.
I'm glad for every piece of advice
That's right, you need to add the jvm library to your build.
Open the project properties dialog.
Select C++ Builder > Settings on the left.
Select the Tool Setting tab.
Select MinGW C++ Linker > Libraries.
Add jvm in the Libraries section.
Add the path to your JDK lib folder in the Libraries Search Path section.
I have a JAVA_HOME environment variable pointing to a JRE subfolder of a JDK so I enter the search path like this: "${env_var:JAVA_HOME}/../lib", with quotes because the environment variable expands to a path with spaces in it.
Now, you might have 32-bit and/or 64-bit JDKs installed. Be sure to use files from the same bit-ness as your compiler output. That includes running your exe with the right jvm.dll first on the DLL search path. On 64-bit Windows, the 32-bit JDK is usually installed under C:\Program Files (x86)\Java....
The #12 says that your compiler is producing 32-bit code (the arguments to JNI_CreateJavaVM are 3 pointers of 4-bytes each) so the linker needs the 32-bit version of jvm.lib rather than the 64-bit version.
BTW—Your java.class.path looks a suspicious for two reasons:
Tests looks like the name of a class. The class path should be a ;-separated list of paths to the root folder of packages. If Tests is a class in the default package, just lop off Tests from your class path.
Eclipse usually sets up Java projects with separate source and binary folders. The paths in the class path are where java should search for .class files. If your project is configured to compile .java files from the src folder to .class files in the bin folder, then replace src with bin in your class path.

Why does the Python/C API crash on PyRun_SimpleFile?

I've been experimenting with embedding different scripting languages in a C++ application, currently I'm trying Stackless Python 3.1. I've tried several tutorials and examples, what few I can find, to try and run a simple script from an application.
Py_Initialize();
FILE* PythonScriptFile = fopen("Python Scripts/Test.py", "r");
if(PythonScriptFile)
{
PyRun_SimpleFile(PythonScriptFile, "Python Scripts/Test.py");
fclose(PythonScriptFile);
}
Py_Finalize();
For some odd reason, running this piece of code results in an access violation at:
PyRun_SimpleFile(PythonScriptFile, "Python Scripts/Test.py");
I've searched online for others with a similar problem and found only one. Their only solution was a workaround that only seems possible in an older version of Python: Creating a python file object and returning the FILE* from that python file object into PyRun_SimpleFile. Such function calls are not available however, the Python 3.1 API creates file objects from a file descriptor and returns file descriptors, but the PyRun_SimpleFile function still requires a FILE*.
I'm at a loss as to how to run any scripts from file, short of loading the entire file into memory manually and running it as a giant string, certainly not a practical solution.
What gives? How can I accomplish this task if the API has an internal error?
Update:
I've managed to build Stackless Python 3.1 from the source and yet the crash remains completely unchanged, despite using the same C runtime library. Both my project and the Stackless Python 3.1 source are built with Visual Studio 2010's C++ compiler and C runtime. I no longer have any inkling as to what might solve this problem, short of modifying Python to use a file name and not a FILE*. Another terrible workaround.
This works for me on Python 3:
PyObject *obj = Py_BuildValue("s", "test.py");
FILE *file = _Py_fopen_obj(obj, "r+");
if(file != NULL) {
PyRun_SimpleFile(file, "test.py");
}
I hope It would be useful.
I was getting a similar crash & did the below:
PyObject* PyFileObject = PyFile_FromString("test.py", "r");
PyRun_SimpleFileEx(PyFile_AsFile(PyFileObject), "test.py", 1);
Note that this was in python 2.7 though. I don't know if the API has changed in 3.x.
Your code works correctly on my installed version of Python 2.6. I also built stackless 3.1.2 from source and it worked correctly. This was with g++ 4.4.3 on Ubuntu 10.04. If you're on windows, you might want to check that both stackless and your code are built against the same C runtime.
This sounds like a problem of mismatched APIs. If your code and the Python runtime were compiled with different compilers, or even different compiler options, then accessing the FILE* could result in an access violation. Can you double-check that you've build your C code properly?
You mention that you're embedding Python into your C++ application. Keep in mind that Python is C code, compiled as C code. Perhaps that is the source of the problem?
If you built your test with VC 2010, you will definitely have problems - VC9 (VS 2008) and VC10 (VS 2010) have mutually incompatible support DLLs that are usually required (implement printf, file i/o and that sort of thing). You cannot mix them if they include the standard libraries, which the python build does.
You always have the option of using gcc (e.g. Cygwin or mingw) or downloading Visual Studio 2008 express, which should work fine for experimentation into python embedding. I have used both with the standard Python 2.7.6 build.
And what about this solution:
Py_SetProgramName(argv[0]);
Py_Initialize();
PyRun_SimpleString("execfile(\"ex30.py\")");
Py_Finalize();
Where ex30.py it the name of the python script I am running.
The below code will execute the test.py module. Python will search the module in the path set. So the path should be handled first.
Py_Initialize();
string path = "Python Scripts/";
//Set the path
PyRun_SimpleString("import sys");
string str = "sys.path.append('" + path + "')";
PyRun_SimpleString(str.c_str());
//Dont use test.py as it actually searches sub module test>>py
PyObject * moduleName = PyUnicode_FromString("test");
PyObject * pluginModule = PyImport_Import(moduleName);
if (pluginModule == nullptr)
{
PyErr_Print();
return "";
}
//Do the executions here
//clean up
Py_DECREF(moduleName);
Py_DECREF(pluginModule);
Py_DECREF(transformFunc);
Py_DECREF(result);
Py_Finalize();