Click here to Skip to main content
15,881,803 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Hi,

I am new to JNI and I have been trying to write a simple program to call Java code from C++.

I am facing problem in creating the JavaVM from C and am getting the Linker error. I have tried changing the GNU library files in dev C++ (I am using Dev c++ compiler) but no luck so far.
The JDK version is 1.6.27.

Can anybody help me and point out the error or recommend a step-by-step approach to write this program?

Here is the C code file I am trying to compile.
C++
#include <jni.h>
#include <windows.h>
 
#define PATH_SEPARATOR ';' /* define it to be ':' on Solaris */
#define USER_CLASSPATH "." /* where Prog.class is */
 
main() {
    JNIEnv *env;
    JavaVM *jvm;
    jint res;
    jclass cls;
    jmethodID mid;
    jstring jstr;
    jclass stringClass;
    jobjectArray args;
 
#ifdef JNI_VERSION_1_2
    JavaVMInitArgs vm_args;
    JavaVMOption options[1];
    options[0].optionString =
        "-Djava.class.path=" USER_CLASSPATH;
    vm_args.version = 0x00010002;
    vm_args.options = options;
    vm_args.nOptions = 1;
    vm_args.ignoreUnrecognized = JNI_TRUE;
    /* Create the Java VM */
    // this should contain the path to the JVM DLL file
    HINSTANCE hVM = LoadLibrary("C:\\Program Files\\Java\\jdk1.6.0_27\\jre6\\bin\\client\\jvm.dll");
if (hVM == NULL)
{
    DWORD dwe = GetLastError();
    return -1;
}
typedef jint	
(CALLBACK *fpCJV)(JavaVM**, void**, JavaVMInitArgs*);

fpCJV CreateJavaVM = (fpCJV)::GetProcAddress(hVM, "JNI_CreateJavaVM");
res = CreateJavaVM(&jvm, (void**)&env, &vm_args);
#else
    JDK1_1InitArgs vm_args;
    char classpath[1024];
    vm_args.version = 0x00010001;
    JNI_GetDefaultJavaVMInitArgs(&vm_args);
    /* Append USER_CLASSPATH to the default system class path */
    sprintf(classpath, "%s%c%s",
            vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH);
    vm_args.classpath = classpath;
    /* Create the Java VM */
    res = JNI_CreateJavaVM(&jvm, &env, &vm_args);
#endif /* JNI_VERSION_1_2 */

    if (res < 0) {
        fprintf(stderr, "Can't create Java VM\n");
        exit(1);
    }
    cls = (*env)->FindClass(env, "Prog");
    if (cls == NULL) {
        goto destroy;
    }

    mid = (*env)->GetStaticMethodID(env, cls, "main",
                                    "([Ljava/lang/String;)V");
    if (mid == NULL) {
        goto destroy;
    }
    jstr = (*env)->NewStringUTF(env, " from C!");
    if (jstr == NULL) {
        goto destroy;
    }
    stringClass = (*env)->FindClass(env, "java/lang/String");
    args = (*env)->NewObjectArray(env, 1, stringClass, jstr);
    if (args == NULL) {
        goto destroy;
    }
    (*env)->CallStaticVoidMethod(env, cls, mid, args);

destroy:
    if ((*env)->ExceptionOccurred(env)) {
        (*env)->ExceptionDescribe(env);
    }
    (*jvm)->DestroyJavaVM(jvm);
}

Could you please check if it gives any compilation error to you?
Posted
Updated 4-Oct-11 22:50pm
v4
Comments
Dalek Dave 4-Oct-11 4:54am    
Edited for Readability.

See here[^] to check your code is following the correct rules. Also, make sure you have defined your JNI calls within extern "C" {} blocks, to prevent name mangling by the compiler.
 
Share this answer
 
Comments
Dalek Dave 4-Oct-11 4:54am    
Good Link
anktrive 4-Oct-11 5:51am    
Hi Richard, Thanks for the quick response.... I have used the exact same code given in the link and used Dev c++ to compile it, so it should follow the rules. In this code only I have received Linker error. I suspect it is not able to locate the java executables on my system.The environment variables on my system are:
PATH: C:\Program Files\Java\jdk1.6.0_27\bin;C:\Program Files\Java\jdk1.6.0_27\jre\bin\client
JAVA_HOME: C:\Program Files\Java\jdk1.6.0_27\bin
LD_LIBRARY_PATH= C:\Program Files\Java\jdk1.6.0_27\jre\bin\client
CLASSPATH= C:\data

Could you please let me know if any of these system variables should be defined differently?
Richard MacCutchan 4-Oct-11 7:18am    
That looks OK, you should also check that the jvm.dll file is accessible in C:\Program Files\Java\jdk1.6.0_27\jre\bin\client.
anktrive 4-Oct-11 7:32am    
I have already checked it since i found it as one of the possible reasons why JVM could not be created. JVM.dll is there. Is there a possibility that it is not accesible at runtime even if it is at the correct location?
Richard MacCutchan 4-Oct-11 8:04am    
Not that I can think of; if you have linked it with the correct .lib file, and the dll is in the Windows path then it should all work. Unfortunately I don't have a ready made test program to try this on my system at the moment. If I get time later I may give it a go.
It's customary to locate the java runtime trough settings from the registry, and then load the dll dynamically.

Have look at:
Jace[^]
Jace offers some additional features you might find interesting too.

[Update]
The _imp__ prefix indicates that you have a problem with your library - I assume you are using MinGW with Dev c++, it's been a while since I played around with it - but if I remember correctly there is a tool provided with MinGW that allows you to create *.a files for dll's based on def files ...

If you're unfamiliar with def files take a look at:
Visual C++ 2010 Express [^] - it's free too.

[Update 2]
Have you read The Java™ Native Interface[^]?

MinGW dlltool[^]

Stdcall and DLL tools of MSVC and MinGW[^]

Best regards
Espen Harlinn
 
Share this answer
 
v3
Comments
Richard MacCutchan 4-Oct-11 7:16am    
I've used JNI quite a bit and never had to do that.
Espen Harlinn 4-Oct-11 10:24am    
It's how the java.exe, Eclipse, and many other tools load the java runtime - and you don't need to have the %JAVA_HOME%\bin directory in your path.
I don't know why this happens BUT:
In your C code, if you replace the line:
C++
res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);

with
C++
// this should contain the path to the JVM DLL file
HINSTANCE hVM = LoadLibrary("C:\\Program Files (x86)\\Java\\jre6\\bin\\client\\jvm.dll");
if (hVM == NULL)
{
    DWORD dwe = GetLastError();
    return -1;
}
typedef jint	(CALLBACK *fpCJV)(JavaVM**, void**, JavaVMInitArgs*);
fpCJV CreateJavaVM = (fpCJV)::GetProcAddress(hVM, "JNI_CreateJavaVM");
res = CreateJavaVM(&jvm, (void**)&env, &vm_args);

it should work; at least it does on my system. I will continue to investigate further as time permits.
 
Share this answer
 
Comments
Espen Harlinn 4-Oct-11 11:35am    
5'ed - "HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment" contains a value called "CurrentVersion" = 1.6 - indicating that one should look at the RuntimeLib value under HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment\1.6 - it contains the full path to the jvm.dll, like "C:\Program Files (x86)\Java\jre6\bin\client\jvm.dll"
Richard MacCutchan 4-Oct-11 12:02pm    
You are correct, of course, it's just that I have always done it like this, without recourse to the registry.
anktrive 5-Oct-11 3:41am    
The following line gives a compilation error as "syntax error before ':' token "
fpCJV CreateJavaVM = (fpCJV)::GetProcAddress(hVM, "JNI_CreateJavaVM");
Am I missing any import header file?
Sorry for the dumb question but i am new to C programming as well.
Richard MacCutchan 5-Oct-11 4:11am    
Did you put the typedef line before it?
anktrive 5-Oct-11 4:14am    
Here is the C code file I am trying to compile.

#include <jni.h>
#include <windows.h>

#define PATH_SEPARATOR ';' /* define it to be ':' on Solaris */
#define USER_CLASSPATH "." /* where Prog.class is */

main() {
JNIEnv *env;
JavaVM *jvm;
jint res;
jclass cls;
jmethodID mid;
jstring jstr;
jclass stringClass;
jobjectArray args;

#ifdef JNI_VERSION_1_2
JavaVMInitArgs vm_args;
JavaVMOption options[1];
options[0].optionString =
"-Djava.class.path=" USER_CLASSPATH;
vm_args.version = 0x00010002;
vm_args.options = options;
vm_args.nOptions = 1;
vm_args.ignoreUnrecognized = JNI_TRUE;
/* Create the Java VM */
// this should contain the path to the JVM DLL file
HINSTANCE hVM = LoadLibrary("C:\\Program Files\\Java\\jdk1.6.0_27\\jre6\\bin\\client\\jvm.dll");
if (hVM == NULL)
{
DWORD dwe = GetLastError();
return -1;
}
typedef jint
(CALLBACK *fpCJV)(JavaVM**, void**, JavaVMInitArgs*);

fpCJV CreateJavaVM = (fpCJV)::GetProcAddress(hVM, "JNI_CreateJavaVM");
res = CreateJavaVM(&jvm, (void**)&env, &vm_args);
#else
JDK1_1InitArgs vm_args;
char classpath[1024];
vm_args.version = 0x00010001;
JNI_GetDefaultJavaVMInitArgs(&vm_args);
/* Append USER_CLASSPATH to the default system class path */
sprintf(classpath, "%s%c%s",
vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH);
vm_args.classpath = classpath;
/* Create the Java VM */
res = JNI_CreateJavaVM(&jvm, &env, &vm_args);
#endif /* JNI_VERSION_1_2 */

if (res < 0) {
fprintf(stderr, "Can't create Java VM\n");
exit(1);
}
cls = (*env)->FindClass(env, "Prog");
if (cls == NULL) {
goto destroy;
}

mid = (*env)->GetStaticMethodID(env, cls, "main",
"([Ljava/lang/String;)V");
if (mid == NULL) {
goto destroy;
}
jstr = (*env)->NewStringUTF(env, " from C!");
if (jstr == NULL) {
goto destroy;
}
stringClass = (*env)->FindClass(env, "java/lang/String");
args = (*env)->NewObjectArray(env, 1, stringClass, jstr);
if (args == NULL) {
goto destroy;
}
(*env)->CallStaticVoidMethod(env, cls, mid, args);

destroy:
if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env);
}
(*jvm)->DestroyJavaVM(jvm);
}
Could you please check if it gives any compilation error to you?
My apologies but I was compiling as C++, which does not work for standard C. Modify the code between the lines #ifdef JNI_VERSION_1_2 and #else as follows:
C++
#ifdef JNI_VERSION_1_2
// definitions required to get the proc address
	HINSTANCE hVM;
	typedef jint	(CALLBACK *fpCJV)(JavaVM**, void**, JavaVMInitArgs*);

	fpCJV CreateJavaVM;
	JavaVMInitArgs vm_args;
	JavaVMOption options[1];
	options[0].optionString =
		"-Djava.class.path=" USER_CLASSPATH;
	vm_args.version = 0x00010002;
	vm_args.options = options;
	vm_args.nOptions = 1;
	vm_args.ignoreUnrecognized = JNI_TRUE;
	/* Create the Java VM */
// load the Java VM dll
	hVM = LoadLibrary("C:\\Program Files (x86)\\Java\\jre6\\bin\\client\\jvm.dll");
	if (hVM == NULL)
	{
		DWORD dwe = GetLastError();
		return -1;
	}
// get the address of the first JNI call and create the VM
	CreateJavaVM = (fpCJV)GetProcAddress(hVM, "JNI_CreateJavaVM");
	res = CreateJavaVM(&jvm, (void**)&env, &vm_args);
#else

I have added some extra comment lines preceding the code that requires changing. You may also like to consider moving to C++ which offers lots of benefits over pure C.


We still have the issue of why the linker cannot find the JNI_CreateJavaVM call, which I may return to as time permits, but at least this solution should allow you to continue working with JNI.

I finally found the time to go and look at this properly, and realised that I have mixed mode libraries on my Windows 7 system. I downloaded the 32bit JDK and now I can link and run successfully. Please check that you are using all 32 bit (from C:\Program Files (x86)\Java) or all 64bit (from C:\Program Files\Java) for your projects.
 
Share this answer
 
v2
Comments
anktrive 7-Oct-11 3:01am    
Thanks Richard.... I was able to get it working using the above code...I think I also have been using 64 bit JDK. will try with 32 bit JDK.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900