How to call Java in C/C ++

Source: Internet
Author: User

How to call Java in C/C ++

Java's cross-platform features make Java more and more popular among developers, but many complaints may also be heard: the graphic user window interface developed using Java jumps out of a console window every time it is started. This console window eclipses a lot of great interfaces. How can I prevent the Java Console window from popping up the GUI program developed through Java? In fact, many popular development environments such as JBuilder and Eclipse are integrated environments developed using pure Java. These integration environments do not open a command window when they are started, because they use JNI (Java Native Interface) technology. With this technology, developers do not have to use the command line to start the Java program. You can write a local GUI program to directly start the Java program, so that you can avoid opening another command window, this makes Java programs more professional.

JNI promises that Java programs running on virtual machines can call each other with programs or class libraries written in other languages (such as C and C ++. At the same time, JNI provides a complete set of APIs that promise to directly embed the Java virtual machine into a local application.

This article describes how to call the Java method in C/C ++, and introduces the development steps and possible difficulties and solutions based on the problems that may be involved. The tool used in this article is Java Development Kit (JDK) 1.3.1 created by Sun and the Visual C ++ 6 Development environment of Microsoft.

Environment Construction

To make the following code work properly, we must establish a complete development environment. First download and install JDK 1.3.1, which is " ". Assume that the installation path is C: JDK. The next step is to set up the integrated development environment and open the option dialog box through Visual C ++ 6 menu Tools → Options.

Add the C: JDKinclude and C: JDKincludewin32 directories to the Include Files directory of the development environment, and add the C: JDKlib directory to the Library Files directory of the development environment. These three directories are header files and library files of some constants, structures, and methods defined by JNI. The integrated development environment has been set up, and the C: JDK jreinclassic directory of the dynamic link library used by the Java Virtual Machine needs to be set to the Path environment variable of the system to execute the program. It should be mentioned that some developers directly copy the DLL files used by jre to the system directory for convenience. This will not work, causing initialization of the Java Virtual Machine environment to fail (Return Value-1), because the Java virtual machine uses a relative path to find the library files and other related files used. So far, the entire JNI development environment has been set up. To make this JNI journey smooth, you must first prepare a Java class. Almost all representative attributes and methods in Java will be used in this class, such as static methods and attributes, arrays, exception throws and capturing. The Java program (Demo. java) We define is as follows. All the code demos in this article are based on this Java program. The Code is as follows:

Package jni. test; /*** this class is used to demonstrate how JNI can access various object attributes and so on * @ author liudong */public class Demo {// It is used to demonstrate how to access static basic type attribute public static int COUNT = 8; // demonstrate the object type attribute public String msg; PRivate int [] counts; public Demo () {this ("default constructor ");} /*** demonstrate how to access the constructor */public Demo (String msg) {System. out. println (":" + msg); this. msg = msg; this. counts = null;}/*** this method demonstrates how to access an access and process Chinese characters */public String getMessage () {return msg ;} /*** demonstrate access to an array object */public int [] getCounts () {return counts ;} /*** demonstrate how to construct an array object */public void setCounts (int [] counts) {this. counts = counts;}/*** demonstrate exception capture */public void throwExcp () throws IllegalaccessException {throw new IllegalAccessException ("exception occur. ");}}

Initialize Virtual Machine

Before calling the Java method, local code must first load the Java virtual machine, and then all Java programs are executed in the virtual machine. To initialize the Java Virtual Machine, JNI provides a series of interface functions called Invocation APIs. Using these APIs, you can easily load virtual machines to the memory. You can use the jint JNI_CreateJavaVM (JavaVM ** pvm, void ** penv, void * args) function to create a VM ). The third parameter in JDK 1.1 always points to a JDK1 _ 1 InitArgs structure, which cannot be seamlessly transplanted in all versions of virtual machines. A standard initialization structure JavaVMInitArgs has been used in JDK 1.2 to replace JDK1_1InitArgs. The following are two sample codes for different versions.

Initialize a virtual machine in JDK 1.1:

# Include int main () {JNIEnv * env; JavaVM * jvm; JDK1_1InitArgs vm_args; jint res;/* IMPORTANT: the version number setting cannot be omitted */vm_args.version = 0x00010001; /* Get the default VM initialization parameter */jni_getdefajavavminitargs (& vm_args);/* Add a custom class path */sprintf (classpath, "% s % c % s ", vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH); vm_args.classpath = classpath;/* set some other initialization parameters * // * Create a VM */res = JNI_CreateJavaVM (& jvm, & env, & vm_args); if (res <0) {fprintf (stderr, "Can't create Java VM"); exit (1 );} /* release VM Resources */(* jvm)-> DestroyJavaVM (jvm );}

Initialize a virtual machine in JDK 1.2:

/* Invoke2.c */# include int main () {int res; JavaVM * jvm; JNIEnv * env; JavaVMInitArgs vm_args; JavaVMOption options [3]; vm_args.version = jni_version_1; // this field must be set to this value/* set the initialization parameter */options [0]. optionString = "-Djava. compiler = NONE "; options [1]. optionString = "-Djava. class. path =. "; options [2]. optionString = "-verbose: jni"; // information used for tracking runtime/* version number setting cannot be missed */vm_args.version = JNI_VERSION_1_2; vm_args.nOptions = 3; vm_args.options = options; vm_args.ignoreUnrecognized = JNI_TRUE; res = JNI_CreateJavaVM (& jvm, (void **) & env, & vm_args); if (res <0) {fprintf (stderr, "Can't create Java VM"); exit (1) ;}( * jvm)-> DestroyJavaVM (jvm); fprintf (stdout, "Java VM destory. ");}


To ensure the portability of JNI code, we recommend that you use JDK 1.2 to create a virtual machine. The second parameter JNIEnv * env of the JNI_CreateJavaVM function is a parameter throughout the beginning and end of JNI, because almost all functions require a parameter JNIEnv * env.


After the Java Virtual Machine is initialized, you can call the Java method. To call a Java object method, follow these steps:

1. Get the class definition of the specified object (jclass)

There are two ways to get the class definition of an object: the first is to use FindClass to find the corresponding class when the class name is known. Note that the class name is not the same as the Java code written at ordinary times. For example, to get the definition of the class jni. test. Demo, the following code must be called:

Jclass cls = (* env)-> FindClass (env, "jni/test/Demo"); // Replace the dot with a slash.

Then, the corresponding class definition is obtained through the object:

Jclass cls = (* env)-> GetObjectClass (env, obj); // Where obj is the object to be referenced and the type is jobject

2. Read the definition of the method to be called (jmethodID)

Let's take a look at the methods defined in JNI:

JmethodID (JNICALL * GetMethodID) (JNIEnv * env, jclass clazz, const char * name, const char * sig); jmethodID (JNICALL * GetStaticMethodID) (JNIEnv * env, jclass, const char * name, const char * sig );

The difference between the two functions is that GetStaticMethodID is used to obtain the definition of static methods, while GetMethodID is used to obtain non-static method definitions. Both functions need to provide four parameters: env is the JNI environment obtained by the VM initialization; the second parameter class is the class definition of the object, that is, the obj obtained by the first step; the third parameter is the method name. The most important parameter is the fourth parameter, which is the definition of the method. Because we know the polymorphism of Methods promised in Java, it is only through the method name that there is no way to locate a specific method, so we need the fourth parameter to specify the specific definition of the method. But how can we use a string to express the specific definition of a method? JDK has prepared a decompilation tool javap, which can be used to get the definition of each attribute and method in the class. Let's take a look at the definition of jni. test. Demo:

Open the command line window and run javap-s-p jni. test. Demo. The running result is as follows:

Compiled from Demo. java public class jni. test. demo extends java. lang. object {public static int COUNT;/* I */public java. lang. string msg;/* Ljava/lang/String; */private int counts [];/* [I */public jni. test. demo ();/* () V */public jni. test. demo (java. lang. string);/* (Ljava/lang/String;) V */public java. lang. string getMessage ();/* () Ljava/lang/String; */public int getCounts () [];/* () [I */public void setCounts (int []);/* ([I) V */public void throwExcp () throws java. lang. illegalAccessException;/* () V */static {};/* () V */}

We can see that there is a comment under each attribute and method in the class. The content that does not contain spaces in the comment is the content to be filled in with the fourth parameter (for details about the javap parameters, see the JDK help ). The following code demonstrates how to access the getMessage method of jni. test. Demo:

/* Suppose we already have a jni. test. demo instance obj */jmethodID mid; jclass cls = (* env)-> GetObjectClass (env, obj); // obtain the instance class definition mid = (* env) -> GetMethodID (env, cls, "getMessage", "() Ljava/lang/String ;"); /* If the value of mid is 0, the method definition cannot be obtained */jstring msg = (* env)-> CallObjectMethod (env, obj, mid ); /* If the method is a static method, you only need to change the last code to the following code: jstring msg = (* env)-> CallStaticObjectMethod (env, cls, mid ); */

3. Call Method

To call a method of an object, you can use the CallMethod function or CallStaticMethod (static method of the callback class), depending on different return types. These methods use the definition of variable parameters. If you need parameters to access a method, you only need to enter all parameters in the Method in order. When talking about constructor access, we will demonstrate how to access constructor with parameters.

Category attributes

The attributes of the category class are basically the same as those of the category class, except that the method is changed to the attribute.

1. Get the class (jclass) of the specified object)

This step is exactly the same as the first step of the category method. For details, see the first step of the category method.

2. Definition of read class attributes (jfieldID)

In JNI, the method for getting class attributes is defined as follows:

JfieldID (JNICALL * GetFieldID) (JNIEnv * env, jclass clazz, const char * name, const char * sig); jfieldID (JNICALL * signature) (JNIEnv * env, jclass clazz, const char * name, const char * sig );

In these two functions, the first parameter is the JNI environment, clazz is the class definition, name is the property name, and the fourth parameter is also used to express the type of the property. When we used the javap tool to obtain the specific definition of a class, there were two rows as follows:

Public java. lang. String msg;/* Ljava/lang/String ;*/

The content in the second line of comment is the information to be filled in with the fourth parameter, which is the same as that in the callback class method.

3. Read and set attribute values

With the attribute definition, it is easy to access the attribute value. There are several methods to read and set the attributes of the class. They are GetField, SetField, GetStaticField, and SetStaticField. For example, GetObjectField can be used to read the msg attribute of the Demo class and GetStaticIntField can be used to access COUNT. The related code is as follows:

JfieldID field = (* env)-> GetFieldID (env, obj, "msg", "Ljava/lang/String;"); jstring msg = (* env) -> GetObjectField (env, cls, field); // msg is the jfieldID field2 = (* env)-> GetStaticFieldID (env, obj, "COUNT ", "I"); jint count = (* env)-> GetStaticIntField (env, cls, field2 );

Access Constructor

Many people often encounter problems in this section when they are new to JNI. After checking the entire jni. h, they can see such a function NewObject, which should be a constructor that can be used for classes. However, this function must provide the method definition of the constructor. Its type is jmethodID. From the previous content, we know that to obtain the definition of a method, we must first know the name of the method. But how can we enter the name of the constructor? In fact, the access constructor is basically the same as accessing a common class method. The only difference is that the method names are different and the method calls are different. The method name must be set to "" When constructing a struct class. The following code demonstrates how to construct a Demo class instance:

Jclass cls = (* env)-> FindClass (env, "jni/test/Demo");/** first, get the Class definition through the Class name, which is equivalent to the Class in Java. forName Method */if (cls = 0) jmethodID mid = (* env)-> GetMethodID (env, cls, "", "(Ljava/lang/String ;) V "); if (mid = 0) jobject demo = jenv-> NewObject (cls, mid, 0 ); /** to access the constructor, you must use the NewObject function to call the previously obtained constructor definition. The above code constructs a Demo instance and transmits an empty string null */

Array Processing

Create a new array

To create an array, we should first know the type and length of the array element. JNI defines the type of a batch of arrays, jArray and the array operation function NewArray, which is the type of elements in the array. For example, to create an integer array with a size of 10 and a location value of 1-10, write the following code:

Int I = 1; jintArray array; // defines an array object (* env)-> NewIntArray (env, 10); for (; I <= 10; I ++) (* env)-> SetIntArrayRegion (env, array, I-1, 1, & I );

Access Data in the array

To access an array, you must first know the length of the array and Its element type. Now we can print each element value in the created array. The Code is as follows:

Int I;/* Get the number of elements of the array object */int len = (* env)-> GetArrayLength (env, array ); /* obtain all elements in the array */jint * elems = (* env)-> GetIntArrayElements (env, array, 0); for (I = 0; I <len; I ++) printf ("ELEMENT % d IS % d", I, elems [I]);

Chinese Processing

The processing of Chinese characters is often a headache, especially for software developed using Java, which is even more prominent in JNI. Because all characters in Java are Unicode encoded, but in local methods, for example, programs written in VC, if there is no special definition, Unicode encoding is generally not used. To enable the local method to access the Chinese characters defined in Java and the Chinese strings generated by accessing the local method in Java, I have defined two methods for mutual conversion.

· Method 1: convert a Java Chinese string to a local string

/** The first parameter is the virtual machine environment pointer. The second parameter is the Java string to be converted. The third parameter is the memory block of the locally stored converted string. The third parameter is the memory block. size */int JStringToChar (JNIEnv * env, jstring str, LPTSTR desc, int desc_len) {int len = 0; if (desc = NULLstr = NULL) return-1; // In VC, wchar_t is used to store the data type wchar_t * w_buffer = new wchar_t [1024]; ZeroMemory (w_buffer, 1024 * sizeof (wchar_t )); // use GetStringChars instead of javaswcscpy (w_buffer, env-> GetStringChars (str, 0); env-> ReleaseStringChars (str, w_buffer); ZeroMemory (desc, desc_len ); // call the character encoding and Conversion Function (Win32 API) to convert UNICODE to an ASCII string. // For details about how to use the function WideCharToMultiByte, see MSDN len = WideCharToMultiByte (CP_ACP, 0, w_buffer, 1024, desc, desc_len, NULL, NULL); // len = wcslen (w_buffer); if (len> 0 & len

· Method 2: Convert the C string to a Unicode string that can be recognized by Java

Jstring NewJString (JNIEnv * env, LPCTSTR str) {if (! Env! Str) return 0; int slen = strlen (str); jchar * buffer = new jchar [slen]; int len = MultiByteToWideChar (CP_ACP, 0, str, strlen (str ), buffer, slen); if (len> 0 & len <slen) buffer [len] = 0; jstring js = env-> NewString (buffer, len ); delete [] buffer; return js ;}


Because Java methods are called, Operation exception information is inevitable. These exceptions cannot be captured through the Exception Processing Mechanism of C ++, but JNI can use some functions to obtain the exception information thrown in Java. Previously, we defined a method throwExcp in the Demo class. The following code accesses this method and captures the thrown exception information:

/** Assume that we have constructed a Demo instance obj and Its Class is defined as cls */jthrowable excp = 0;/* exception information definition */jmethodID mid = (* env) -> GetMethodID (env, cls, "throwExcp", "() V");/* If mid is 0, the method definition fails to be obtained */jstring msg = (* env) -> CallVoidMethod (env, obj, mid);/* after this method is called, an IllegalAccessException exception will be thrown */excp = (* env)-> ExceptionOccurred (env ); if (excp) {(* env)-> ExceptionClear (env); // obtain exception information by accessing excp/* in Java, most of the exception information is extended java. lang. exception, so you can access Excp toString or getMessage to obtain the exception information. The methods for accessing these two methods are the same as those for how to restore the class. */}

Thread and synchronous access

In some cases, you need to use multiple threads to access Java. We know that a Java Virtual Machine consumes a lot of system memory resources, and each virtual machine requires about 20 mb of memory. To save resources, each thread must use the same virtual machine. In this way, only one virtual machine needs to be initialized in the entire JNI program. This is what everyone thinks. But once the sub-thread accesses the virtual machine environment variable created by the main thread, the system displays an error dialog box and the entire program is terminated.

In fact, this involves two concepts: Virtual Machine (JavaVM * jvm) and virtual machine environment (JNIEnv * env ). Jvm rather than env is the one that actually consumes a lot of system resources. jvm promises to access multiple threads, but env can only be accessed by the thread that creates it, in addition, each thread must create its own virtual machine environment env. At this time, some people may ask, the main thread creates the virtual machine environment env when initializing the virtual machine. To allow the subthread to create its own env, JNI provides two functions: AttachCurrentThread and DetachCurrentThread. The following code is the framework of the sub-thread to access the Java method:

DWord WINAPI ThreadProc (PVOID dwParam) {JavaVM jvm = (JavaVM *) dwParam;/* pass the VM through parameters */JNIEnv * env; (* jvm)-> AttachCurrentThread (jvm, (void **) & env, NULL );......... (* jvm)-> DetachCurrentThread (jvm );}


The topic about time is a problem I encountered in actual development. To release a program that uses JNI, you are not required to install a Java Runtime Environment, because you can package the runtime environment in the installer. To make the package program easier to download, this package should be relatively small, so some unnecessary files in the JRE (Java Runtime Environment) should be removed. However, if the Calendar type in Java is used in the program, such as java. util. Calendar, there must be a file that cannot be removed. This file is [JRE] lib zmappings. It is a time zone ing file. Without this file, you will find that the time operation often differs from the correct time by several hours. The following is a list of essential files in JRE packaging (taking Windows as an example). [JRE] is the directory of the running environment, and the relative paths between these files cannot be changed.

File Name directory hpi. dll [JRE] in ioser12.dll [JRE] in java. dll [JRE] in net. dll [JRE] in verify. dll [JRE] in zip. dll [JRE] in jvm. dll [JRE] inclassic rt. jar [JRE] lib tzmappings [JRE] lib

Since rt. jar is about 10 MB, but a large part of the files are not required, you can delete them according to the actual application. For example, if the program does not use Java Swing, you can delete all files involved in Swing and repackage them.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.