Java's cross-platform features have made Java more popular with developers, but there are often complaints that the GUI-developed graphical user interface will jump out of a console window every time it starts up, and this console window makes a great interface a bit more of a splash. How can I make GUI programs developed by Java do not pop up the Java console window? In fact, many popular development environments, such as JBuilder and Eclipse, are integrated environments that are developed using pure java. These integration environments do not open a command window when they are started, because it uses the technology of JNI (Java Native Interface). With this technique, the developer does not have to use the command line to start the Java program, it can start the Java program directly by writing a local GUI program, which avoids having to open another command window to make the Java program developed more professional.
JNI promises that Java programs running on virtual machines can make calls to 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 allow the Java virtual machine to be embedded directly into the local application.
This article will show you how to call Java methods in C + + and introduce the entire development steps and possible challenges and workarounds in conjunction with the issues that may be involved. The tools used in this article are the Java development Kit (JDK) version 1.3.1 created by Sun and Microsoft's Visual C + + 6 development environment.
Environment construction
In order for the code in the following sections of this article to work properly, we must establish a complete development environment. You first need to download and install the JDK 1.3.1, which is "http://java.sun.com". Assume that the installation path is C:JDK. The next step is to set up the integrated development environment and open the Options dialog box through the Visual C + + 6 menu tools→options.
Add the catalog C:jdkinclude and C:jdkincludewin32 to the Include files directory for the development environment, adding the C:jdklib directory to the library files directory of the development environment. These three directories are header and library files for some constants, structures, and methods defined by JNI. The integrated development environment is already set up, and in order to execute the program it is necessary to set the directory C:jdk jreinclassic of the dynamic link library used by the Java Virtual machine to the system's PATH environment variable. What needs to be proposed here is that some developers directly copy the DLL files used by the JRE directly to the system directory for convenience. This does not work, causing the initialization of the Java Virtual Machine environment to fail (return value-1) Because the Java virtual machine is looking for the library file and some other related files in the relative path. The entire JNI development environment is set up and a Java class must be prepared in order for this JNI journey to proceed smoothly. In this class, almost all of the representative properties and methods in Java are used, such as static methods and properties, arrays, exception throws and snaps, and so on. The Java Program (Demo.java) We define is as follows, and all the code demos in this article will be based on the Java program, with the following code:
package jni.test; /** * This class is to demonstrate how JNI accesses various object properties such as * @author liudong */ public class demo { //is used to demonstrate how to access static basic type properties public static int count = 8; //Presentation Object Type Properties public String msg; PRivate int[] counts; Public demo () { this ("default constructor"); } /** * demonstrates 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 the processing of Chinese characters */ public string getmessage () { return msg; } /** * demonstrates access to array objects */ public int[] getcounts () { return counts; } /** * Demonstrates how to construct an array object */ public void setcounts (int[] counts) { this.counts = counts; } /** * Demo exception Capture  */ PUBLIC VOID THROWEXCP () throws IllegalaccessException { Throw new illegalaccessexception ("exception occur."); } }
Initializing a virtual machine
Native code must load the Java Virtual machine before invoking the Java method, and all Java programs are executed in the virtual machine. To initialize a Java Virtual machine, JNI provides a series of interface functions invocation APIs. These APIs make it easy to load virtual machines into memory. Virtual machines can be created with functions Jint JNI_CREATEJAVAVM (JAVAVM **pvm, void **penv, void *args). One thing to note about this function is that the third parameter in JDK 1.1 always points to a struct jdk1_ 1InitArgs, which cannot be fully ported across all versions of virtual machines. A standard initialization structure, Javavminitargs, has been used in JDK 1.2 to replace Jdk1_1initargs. Below we give two different versions of the sample code, respectively.
Initialize the virtual machine in JDK 1.1:
#include int main () {jnienv *env; JAVAVM *JVM; Jdk1_1initargs Vm_args; Jint Res; /* IMPORTANT: The version number setting must not be missed */vm_args.version = 0x00010001; /* Get default virtual machine initialization parameters */Jni_getdefaultjavavminitargs (&vm_args); /* Add the custom Classpath */sprintf (classpath, "%s%c%s", Vm_args.classpath, Path_separator, User_classpath); Vm_args.classpath = Classpath; /* Set some additional initialization parameters */* Create virtual machine */Res = JNI_CREATEJAVAVM (&jvm,&env,&vm_args); if (Res < 0) {fprintf (stderr, "Can ' t create Java VM"); Exit (1); }/* Frees the virtual machine resource */(*JVM)->DESTROYJAVAVM (JVM); }
JDK 1.2 initializes the virtual machine:
/* invoke2.c */ #include int main () { int res; JavaVM *jvm; jnienv *env; javavminitargs vm_args; javavmoption options[3]; vm_ args.version=jni_version_1_2;//This field must be set to this value /* set initialization parameters */ options[0].optionstring = "- Djava.compiler=none "; options[1].optionstring = "-djava.class.path=. "; options[2].optionString = "-verbose:jni";//For tracking runtime information /* version number setting cannot be missed */ vm_args.version = JNI_VERSION_1_2; vm_args.nOptions = 3; vm_args.options = options; &NBSP;VM_ARGS.IGNOREUNRECOGNIZED&NBSP;=&NBSP;JNI_TRUE;&NBSP;RES&NBSP;=&NBSP;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 portability of JNI code, we recommend that you use JDK 1.2 to create a virtual machine. The second parameter of the JNI_CREATEJAVAVM function, JNIEnv *env, is a parameter that runs through the entire JNI, because almost all functions require that a parameter be jnienv *env.
Accessing class methods
Once the Java virtual machine is initialized, you can start calling Java methods. The method to invoke a Java object must go through several steps:
1. Get the class definition for 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 in the case of a known class name. However, to focus on the class name and different from the usual Java code written, such as to get the definition of class Jni.test.Demo must call the following code:
Jclass cls = (*env)->findclass (env, "Jni/test/demo");//Change the dot to a slash
The corresponding class definition is then obtained directly from the object:
Jclass cls = (*env), Getobjectclass (env, obj); Where obj is the object to be referenced, the type is Jobject
2. Read the definition of the method to invoke (Jmethodid)
Let's take a look at the function that gets the method definition in JNI:
Jmethodid (Jnicall *getmethodid) (jnienv *env, Jclass clazz, const char *name, const char *sig); Jmethodid (Jnicall *getstaticmethodid) (jnienv *env, Jclass class, const char *name, const char *sig);
The difference between these two functions is that getstaticmethodid is used to get the definition of a static method, and Getmethodid is to get a non-static method definition. Both functions need to provide four parameters: env is the JNI environment that initializes the virtual machine, the second parameter class is the class definition of the object, the first step is the obj, the third parameter is the method name, and the most important is the fourth parameter, which is the definition of the method. Because we know the polymorphism of the promised method in Java, there is no way to navigate to a specific method by means of a method name, and a fourth parameter is required to specify the specific definition of the method. But how do you use a string to represent a specific definition of a method? The JDK has been prepared with the good one anti-compilation tool JAVAP, which gives you the ability to define each property and method in the class. Here's a look at the definition of Jni.test.Demo:
Open a command-line window and run Javap-s-P jni.test.Demo to get the result running 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 &NBSP;THROWEXCP () throws java.lang.IllegalAccessException; /* () v */ static {}; /* () v */ }
We see a comment below each property and method in the class. The contents of the note that do not contain spaces are the fourth parameter to fill in (for JAVAP specific parameters please check the JDK's use help). The following code shows how to access the Jni.test.Demo GetMessage method:
/* Suppose we already have a Jni.test.Demo instance of obj */jmethodid mid; Jclass cls = (*env)-Getobjectclass (env, obj);//Gets the class definition of the instance mid= (*env)->getmethodid (env,cls, "GetMessage", "() ljava/lang/string; "); /* If mid is 0 means the Get method definition failed */jstring msg = (*env)-Callobjectmethod (env, obj, mid); /* If the method is static, then just change the last sentence code to the following: jstring msg = (*env)-Callstaticobjectmethod (Env, CLS, mid); */
3. Calling methods
To invoke a method of an object, you can use the function Callmethod or Callstaticmethod (the static method that accesses the class), depending on the return type. All of these methods use the definition of a mutable parameter, and if you access a method that requires parameters, simply fill in the method with all the parameters in the order. When you talk about access to a constructor, you show how to access the constructor with parameters.
Accessing class Properties
Accessing a class's properties is generally consistent with the way the class is accessed, but only by turning the method into a property.
1. Get the class of the specified object (jclass)
This step is identical to the first step in accessing the class method, using the first step of accessing the class method.
2. Read the definition of the class attribute (Jfieldid)
In Jni, this defines methods that get the properties of a class:
Jfieldid (Jnicall *getfieldid) (jnienv *env, Jclass clazz, const char *name, const char *sig); Jfieldid (Jnicall *getstaticfieldid) (jnienv *env, Jclass clazz, const char *name, const char *sig);
The first parameter in these two functions is the JNI environment; The clazz is the definition of the class; name is the attribute, and the fourth argument is the same as the type of the property. Before we use the JAVAP tool to get a concrete definition of a class, there are two lines:
Public java.lang.String msg; /* ljava/lang/string; */
The second line of comments is the fourth parameter to fill in the information, which is the same as accessing the class method.
3. Reading and setting property values
With the definition of a property, it is easy to access the property value. There are several ways to read and set the properties of a class, which are: GetField, SetField, Getstaticfield, Setstaticfield. such as reading the Msg property of the demo class can be used Getobjectfield, and access to count with Getstaticintfield, the relevant code is as follows:
Jfieldid field = (*env)->getfieldid (env,obj, "msg", "ljava/lang/string;"); jstring msg = (*env)-Getobjectfield (Env, CLS, field);//msg is the corresponding demo msg jfieldid field2 = (*env) Getstaticfieldid (Env,obj, "COUNT", "I"); Jint count = (*env)->getstaticintfield (ENV,CLS,FIELD2);
Access constructors
A lot of people have just come into contact with JNI and often encounter problems in this section, looked through the whole jni.h see a function newobject, it should be used to access the class constructor. However, the function needs to provide a method definition for the constructor, whose type is Jmethodid. From the previous content we know that to get the definition of a method the first thing to know is the name of the method, but how to fill in the name of the constructor? In fact, access to the constructor and access to a common class method is basically the same, the only difference is that the method name is different and the method call is different. The method name must be filled in "" when accessing the constructor of a class. The following code shows how to construct an instance of a demo class:
Jclass cls = (*env)->findclass (env, "Jni/test/demo"); /** first gets the definition of the class through the name of the class, equivalent to the Class.forName method in Java */if (CLS = = 0) Jmethodid mid = (*env)->getmethodid (Env,cls, "", "(ljava/ lang/string;) V "); if (mid = = 0) jobject demo = Jenv->newobject (cls,mid,0); The/** access constructor must use the NewObject function to invoke the preceding code for the definition of the constructor that was obtained. We constructed a demo instance and passed an empty string null */
Array processing
Create a new array
To create an array, we should first know the type of the array element and the array length. JNI defines the type jarray of a batch of arrays and the function NewArray of array operations, which is the type of the elements in the array. For example, to create an array of integers with a size of 10 and a value of 1-10 for each position, write the following code:
int i = 1; Jintarray array;//Definition Array Object (*ENV), Newintarray (env, 10); for (; i<=; i++) (*env)->setintarrayregion (env, Array, i-1, 1, &i);
Accessing the data in an array
Accessing an array should first know the length of the array and the type of the element. Now let's print out the values of each element in the created array, and the code is as follows:
int i; /* Gets the number of elements of an array object */int len = (*env)->getarraylength (env, array); /* Get all the 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, not all software developed in the Java language, the problem of JNI is more prominent. Since all characters in Java are Unicode encoded, in a local method, such as a program written with VC, if no extraordinary definition is generally not using Unicode encoding. In order for local methods to access the Chinese characters defined in Java and Java to access native methods, I have defined two methods for converting to one another.
· Method One, convert the Java Chinese string to a local string
/** The first parameter is the environment pointer of the virtual machine 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 size of the memory block */int Jstringtochar (jnienv *env, jstring str, LPTSTR desc, int desc_len) {int len = 0; if (desc==nullstr==null) return-1;//wchar_t in VC is the data type used to store wide-byte characters (UNICODE) wchar_t *w_buffer = new wchar_t[1024]; ZeroMemory (w_buffer,1024*sizeof (wchar_t)); Use Getstringchars instead of Getstringutfchars wcscpy (W_buffer,env->getstringchars (str,0)); Env->releasestringchars (Str,w_buffer); ZeroMemory (Desc,desc_len); Call the character encoding conversion function (Win32 API) to convert Unicode to ASCII encoded format string//for use of function WideCharToMultiByte refer to MSDN len = WideCharToMultiByte (CP_ACP, 0,w_buffer,1024,desc,desc_len,null,null); Len = Wcslen (W_buffer); if (len>0 && len
· Method Two, convert C's string to a Unicode string that Java recognizes
Jstring newjstring (jnienv* env,lpctstr str) {if (!env!str) return 0; int slen = strlen (str); jchar* buffer = new JCHAR[SL EN]; 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; }
Abnormal
Because the Java method is called, it is unavoidable to produce exception information for the operation. There is no way to capture these exceptions through the exception handling mechanism of C + + itself, but JNI can use some functions to get exception information thrown in Java. Before we defined a method THROWEXCP in the demo class, the following will access the method and catch the exception information thrown out, the code is as follows:
/** assume that we have constructed a demo instance of obj whose class is defined as CLS */jthrowable EXCP = 0;/* exception Information definition */jmethodid mid= (*env)->getmethodid (Env,cls, " THROWEXCP "," () V "); /* If mid is 0 means the Get method definition failed */jstring msg = (*env)-Callvoidmethod (env, obj, mid); /* A Illegalaccessexception exception is thrown after the method is called */EXCP = (*env)->exceptionoccurred (env); if (EXCP) {(*env)->exceptionclear (env);//access EXCP to get specific exception information/* in Java, Most of the exception information is extended class java.lang.Exception, so you can access EXCP's ToString or getmessage to get the contents of the exception information. Access to these two methods is the same as the previous method of how to access the class. */ }
Threads and synchronous access
There are times when you need to use multithreading to access Java's methods. We know that a Java virtual machine is a very consuming system of memory resources, almost every virtual machine needs about 20MB of memory. To conserve resources, each thread is required to use the same virtual machine, so that only one virtual machine can be initialized in the entire JNI program. Everyone thinks so, but once the child thread accesses the virtual machine environment variable created by the main thread, an error dialog box appears and the entire program terminates.
In fact, there are two concepts involved, namely virtual machines (JAVAVM *JVM) and virtual machine environments (jnienv *env). What really consumes a lot of system resources is the JVM, not ENV,JVM, that promises to be accessed by multiple threads, but env can only be accessed by the thread that created it itself, and each thread must create its own virtual machine environment, Env. At this point, it will be questioned, the main thread in the initialization of the virtual machine created a virtual machine environment env. In order for a child thread to be able to create its own env,jni, two functions are provided: Attachcurrentthread and Detachcurrentthread. The following code is a skeleton of a child thread accessing a Java method:
DWord WINAPI ThreadProc (PVOID dwparam) {JAVAVM JVM = (javavm*) dwparam;/* pass the virtual machine through parameters */jnienv* env; (*JVM)-Attachcurrentthread (JVM, (void**) &env, NULL); ......... (*JVM), Detachcurrentthread (JVM); }
Time
The topic of time is a problem I encountered in the actual development. When you publish a program that uses JNI, it does not necessarily require the customer to install a Java runtime environment, because the runtime can be packaged in the installer. In order for the packager to be useful for downloading, this package is smaller, so remove some unnecessary files from the JRE (Java Runtime). However, if the program is used in Java calendar type, such as Java.util.Calendar, then a file must not be removed, this file is [Jre]lib zmappings. It is a time zone mapping file, and once the file is not there, it is found that the time operation often occurs several hours apart from the correct time. The following is a list of the necessary files in the packaged JRE (for example in the Windows environment) where [JRE] is the directory of the running environment and the relative paths between these files cannot be changed.
FileName 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 the Rt.jar has almost 10MB, there are a large portion of the files that are not required and can be deleted according to the actual application situation. For example, if a program does not use Java Swing, it can remove files that involve Swing and then repackage them.
How to call Java in C/C + +