Snake
Snake

Reputation: 43

Fatal error when using jni

I'm trying to use jni to call java methods from c++. actually more of a callback (java -> c++ -> java) I've checked the c++ program for errors by testing it in .exe (c++ -> java) The program works perfect in visual studio. but fails and crashes when I convert it to dll and use it in java. I think it's related to jvm.dll because I had to include it to my visual studio project.

c++:

#include <stdio.h>
#include <jni.h>
#include <string.h>
#include "Inter.h"

JNIEnv* create_vm(JavaVM ** jvm) {

    JNIEnv *env;
    JavaVMInitArgs vm_args;
    JavaVMOption options;
    options.optionString = "-Djava.class.path=C:\\Users\\SolidSnake\\workspace\\Test\\bin"; //Path to the java source code
    vm_args.version = JNI_VERSION_1_8;
    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;
}

void callMethod() {
    JNIEnv *env;
    JavaVM * jvm;
    env = create_vm(&jvm);
    jclass m;
    jmethodID test;
    m = env->FindClass("Main");
    test = env->GetStaticMethodID(m,"callbackFromC","()V");
    env->CallStaticVoidMethod(m,test);
}

java:

public final class Main {

    public native int callToC();

    public static void callbackFromC() {
        System.out.println("Hello from C!");
    }

    public static void main(String[] args) {
        System.loadLibrary("Test");
        new Main().callToC();
    }

}

crash:

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x000007f9aed32ff8, pid=4016, tid=8228
#
# JRE version: Java(TM) SE Runtime Environment (8.0_11-b12) (build 1.8.0_11-b12)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.11-b03 mixed mode windows-amd64 compressed oops)
# Problematic frame:
# C  [Test.dll+0x1a08]  JNIEnv_::FindClass+0x28
#
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows

Here's how the program goes (j:callToC) -> (c:callMethod) -> (j:callbackFromC)

Upvotes: 2

Views: 2017

Answers (1)

anton.vodostoev
anton.vodostoev

Reputation: 310

I had the same problem a couple of days ago and I have found a solution (maybe not the best one) of the problem and now I proud to share it (this one probably would be useful for somebody in the future).

Here is my code that worked well when I had only two projects (C++->Java), that is, the C++-project invoking Java-methods:

void JVM:: Init( const std:: string& javaClassesPath )  {    
    std:: string optionString = ( std:: string("-Djava.class.path=") + javaClassesPath );
    JavaVMOption options_;  
    options_.optionString = const_cast<char*>( optionString.c_str() );

    // initializing of JVM initial arguments:
    JavaVMInitArgs arguments_;
    memset( &arguments_, 0, sizeof( arguments_ ) );
    arguments_.version = JNI_VERSION_1_6;
    arguments_.nOptions = 1;
    arguments_.options = &options_;
    arguments_.ignoreUnrecognized = 0;

    // creating JVM:
    long status = JNI_CreateJavaVM( &jvm_, (void**)&env_, &arguments_ );    
    if ( status == JNI_ERR )        
        throw std:: exception("Error: unable to create Java Virtual Machine.\n");

    FindClass("ChartBuilder");
}

This code was being invoked the following way:

JVM JChartBuilder( "D:\\Java Src\\Chart\\Chart.jar" ); // the path that will be used as classpath when creating VM (unless this path is specified it will be unable to find the class ChartBuilder)

Then (Java->C++Java) When I got the third project (Java) that must invoke methods from that C++-project using JNI, I faced the problem that I cannot create VM when one has already run in the current process. But we can attach to existing VM! So, the code listed above can be changed to suit this requirements:

void JVM:: Init( const std:: string& javaClassesPath )
{
    // initializing of JVM options: 
    std:: string optionString = ( std:: string("-Djava.class.path=") + javaClassesPath );
    JavaVMOption options_;  
    options_.optionString = const_cast<char*>( optionString.c_str() );

    // initializing of JVM initial arguments:
    JavaVMInitArgs arguments_;
    memset( &arguments_, 0, sizeof( arguments_ ) );
    arguments_.version = JNI_VERSION_1_6;
    arguments_.nOptions = 1;
    arguments_.options = &options_;
    arguments_.ignoreUnrecognized = 0;

    // creating JVM or attaching to JVM:
    long status;
    /* is there any running VMs in the process? */
    JavaVM* createdVMs[1] = { nullptr };
    jsize numberOfVMs;
    status = JNI_GetCreatedJavaVMs(createdVMs, 1, &numberOfVMs);
    /* END OF is there any running VMs in the process? */
    if( numberOfVMs != 0 )
    {   // if VM already runs:
        jvm_ = createdVMs[0]; // get the VM
        jvm_->AttachCurrentThread((void**)&env_, NULL); // attach to the VM
    }
    else
    {   // if VM does not run:
        status = JNI_CreateJavaVM( &jvm_, (void**)&env_, &arguments_ ); 
    }

    if ( status == JNI_ERR )        
        throw std:: exception("Error: unable to create Java Virtual Machine.\n");

    FindClass("ChartBuilder");
}

But that's not all yet. When running main Java-project (the first in Java->C++->Java), I got the following problem: unable to find specified class ChartBuilder. That is I attached to existing VM, but this virtual machine does not know about the path to this class. Remember, that when I created VM in C++, I specified the path explicitly. To solve this problem I have now to specify the classpath additionaly when running main Java-project.

That is all. The only thing I don't like is the need to specify the classpath to ChartBuilder when running main Java-project, but this one is not critical for me, though I'd like to find out how to avoid this need. Maybe somebody knows?

Upvotes: 1

Related Questions