Implementing Java Call DLLs in Windows (reprint)

Source: Internet
Author: User

This article provides examples of Java code that calls local C code, including passing and returning some commonly used data types. The local method is included in the platform-specific executable file. For the example in this article, the local method is included in a Windows 32-bit dynamic-link library (DLL).

But I'd like to remind you that calls outside of Java are usually not ported to other platforms, and security exceptions can be thrown in applets. Implementing native code will make your Java application fail to pass 100% pure Java testing. However, there are several guidelines to consider if you must perform a local call:

    1. Encapsulate all your local methods in a single class that calls a single DLL. For each target operating system, you can replace the DLL with a version that is specific to the appropriate platform. This minimizes the impact of local code and helps to include the porting issues that you need later.
    2. Local methods are simple. Try to minimize the dependency of your DLLs on any third-party (including Microsoft) run-time DLLs. Keep your local methods as independent as possible, minimizing the overhead required to load your DLLs and applications. If run-time DLLs are required, they must be supplied with the application.

Java Call C

For Java methods that call C functions, a local method must be declared in the Java class. In all the examples in this section, we will create a class named Mynative and gradually add the new functionality to it. This emphasizes the idea of concentrating local methods in a single class to minimize the porting effort needed later.

Example 1--passing parameters

In the first example, we pass three common parameter types to local functions: String, int , and boolean . This example shows how these parameters are referenced in the local C code.

public class Mynative
{
public void Showparms (String s, int i, Boolean b)
{
ShowParms0 (S, I, b);
}
Private native void ShowParms0 (String s, int i, Boolean b);
Static
{
System.loadlibrary ("mynative");
}
}

Note that the local method is declared private, and a wrapper method is created for common purposes. This further isolates the local method from the rest of the code, allowing it to be optimized for the desired platform. The static clause loads the DLL that contains the local method implementation.

The next step is to generate C code to implement the ShowParms0 method. The C function prototype of this method is created by using the Javah utility with the. class file, and the. class file is generated by compiling the Mynative.java file. This utility can be found in the JDK. Here is the usage of Javah:




This will generate a MyNative.h file that contains a native method prototype, as follows:

/*
* Class: mynative
* Method: showParms0
* Signature: (Ljava/lang/string;iz) V
*/
jniexport void Jnicall Java_mynative_showparms0
(JNIEnv *, Jobject, jstring, Jint, Jboolean);

The first parameter is the JNI environment pointer used when invoking the Jni method. The second parameter is a handle to the Java object mynative instantiated in this Java code. Other parameters are parameters of the method itself. Please note that MyNative.h includes header file jni.h. Jni.h contains prototypes and other declarations for JNI APIs and variable types (including Jobject, jstring, Jint, Jboolean, and so on).

The local method is implemented in the file mynative.c in the C language:

#include <stdio.h>
#include "MyNative.h"
jniexport void Jnicall Java_mynative_showparms0
(jnienv *env, Jobject obj, jstring s, jint I, Jboolean b)
{
Const char* SZSTR = (*env)->getstringutfchars (env, S, 0);
printf ("String = [%s]\n", szstr);
printf ("int =%d\n", i);
printf ("Boolean =%s\n", (b==jni_true?) "True": "false"));
(*env)->releasestringutfchars (env, S, SZSTR);
}

JNI Api,getstringutfchars, used to create a C string based on a Java string or jstring parameter. This is required because the Java string cannot be read directly in the local code, but must be converted to a C string or Unicode. For more information about converting Java strings, see the article titled NLS Strings and JNI. However, Jboolean and jint values can be used directly.

MyNative.dll is created by compiling a C source file. The following compilation statement uses the Microsoft Visual C + + compiler:


-femynative.dll

Where c:\jdk1.1.6 is the installation path of the JDK.

MyNative.dll has been created and can now be used for the Mynative class.
You can test this local method in this way: Create a Main method in the Mynative class to invoke the Showparms method, as follows:

   public static void Main (string[] args)
{
mynative obj = new mynative ();
Obj.showparms ("Hello", +, True);
Obj.showparms ("World", and false);
}

When running this Java application, make sure that MyNative.dll is located in the path specified by the Windows PATH environment variable or under the current directory. When executing this Java program, if the DLL is not found, you may see the following message:

Java mynative  
Can ' t find class mynative

This is because the static clause cannot load the DLL, so an exception is thrown when initializing the Mynative class. The Java interpreter handles this exception and reports a generic error stating that the class could not be found.
If you run the interpreter with the-verbose command-line option, you will see that it loads the unsatisfiedlinkerror exception because it cannot find the DLL.

If this Java program finishes running, it will output the following:

Java mynative  
String = [Hello]

Boolean = True
String = [World]

= 34

boolean = falseExample 2--Returns a value

This example shows how to implement the return code in a local method.
Add this method to the Mynative class, and the class now becomes the following form:

public class Mynative
{
public void Showparms (String s, int i, Boolean b)
{
ShowParms0 (S, I, b);
}
public int hypotenuse (int a, int b)
{
Return Hyptenuse0 (A, b);
}
Private native void ShowParms0 (String s, int i, Boolean b);
Private native int hypotenuse0 (int a, int b);
Static
{
System.loadlibrary ("mynative");
}
/* Test Local method */
public static void Main (string[] args)
{
mynative obj = new mynative ();
System.out.println (Obj.hypotenuse (3,4));
System.out.println (Obj.hypotenuse (9,12));
}
}

The common Hypotenuse method calls the local method hypotenuse0 to calculate the value based on the passed parameters and returns the result as an integer. The prototype of this new native method is generated using Javah. Note that each time you run this utility, it will automatically overwrite the MyNative.h in the current directory. Execute the Javah as follows:

Javah-jni mynative  

The resulting MyNative.h now contains the HYPOTENUSE0 prototype, as follows:

/*
* Class: mynative
* Method: hypotenuse0
* Signature: (II) I
*/
Jniexport Jint Jnicall Java_mynative_hypotenuse0
(JNIEnv *, Jobject, Jint, Jint);

The method is implemented in the MYNATIVE.C source file, as follows:

#include <stdio.h>
#include <math.h>
#include "MyNative.h"
jniexport void Jnicall Java_mynative_showparms0
(jnienv *env, Jobject obj, jstring s, jint I, Jboolean b)
{
Const char* SZSTR = (*env)->getstringutfchars (env, S, 0);
printf ("String = [%s]\n", szstr);
printf ("int =%d\n", i);
printf ("Boolean =%s\n", (b==jni_true?) "True": "false"));
(*env)->releasestringutfchars (env, S, SZSTR);
}
Jniexport Jint Jnicall Java_mynative_hypotenuse0
(jnienv *env, Jobject obj, jint A, jint b)
{
int RTN = (int) sqrt ((double) ((a*a) + (b*b));
Return (Jint) Rtn;
}

Again, be aware that the jint and int values are interchangeable.
Recompile the DLL with the same compilation statement:


-femynative.dll

Now execute Java mynative will output 5 and 15 as the hypotenuse value.

Example 3--static method

You may have noticed in the above example that the instantiated Mynative object is not necessary. Practical methods typically do not require actual objects, and they are usually created as static methods. This example shows how to implement the above example with a static method. Change the method signatures in Mynative.java so that they become static methods:

  public static int hypotenuse (int a, int b)
{
Return Hypotenuse0 (A, b);
}
...
private static native int hypotenuse0 (int a, int b);

Now run Javah to create a new prototype for Hypotenuse0 , and the resulting prototype is as follows:

/*
* Class: mynative
* Method: hypotenuse0
* Signature: (II) I
*/
Jniexport Jint Jnicall Java_mynative_hypotenuse0
(JNIEnv *, Jclass, Jint, Jint);

The method signature in the C source code is changed, but the code remains the same:

Jniexport Jint Jnicall Java_mynative_hypotenuse0
(JNIEnv *env, Jclass cls, jint A, jint b)
{
int RTN = (int) sqrt ((double) ((a*a) + (b*b));
Return (Jint) Rtn;
}

Essentially, the Jobject parameter has become the Jclass parameter. This parameter is a handle to the mynative.class. The main method can be changed to the following form:

  public static void Main (string[] args)
{
System.out.println (Mynative.hypotenuse (3, 4));
System.out.println (Mynative.hypotenuse (9, 12));
}

Because the method is static, calling it does not require instantiating the Mynative object. The examples later in this article will use static methods.

Example 4--Passing an array

This example shows how to pass an array-type parameter. This example uses a basic type, Boolean, and changes the array element. The next example accesses an array of String (non-primitive types). Add the following method to the Mynative.java source code:

  public static void SetArray (boolean[] ba)
{
for (int i=0; i < ba.length; i++)
Ba[i] = true;
SetArray0 (BA);
}
...
private static native void SetArray0 (boolean[] ba);

In this case, the Boolean array is initialized to true, and the local method sets the specific element to false. At the same time, in the Java source code, we can change main to include the test code:

    boolean[] ba = new boolean[5];
Mynative.setarray (BA);
for (int i=0; i < ba.length; i++)
System.out.println (Ba[i]);

After compiling the source code and executing Javah, the MyNative.h header file contains the following prototypes:

/*
* Class: mynative
* Method: setArray0
* Signature: ([Z) V
*/
jniexport void Jnicall Java_mynative_setarray0
(JNIEnv *, Jclass, Jbooleanarray);

Note that the Boolean array is created as a single type named Jbooleanarray.
The base types have their own array types, such as Jintarray and Jchararray.
Arrays of non-basic types use the Jobjectarray type. The next example includes a Jobjectarray. The array elements of this Boolean array are accessed through the JNI method getbooleanarrayelements.
There are equivalent methods for each of the basic types. This local method is implemented as follows:

jniexport void Jnicall Java_mynative_setarray0
(JNIEnv *env, Jclass cls, Jbooleanarray BA)
{
jboolean* PBA = (*env)->getbooleanarrayelements (env, BA, 0);
Jsize len = (*env)->getarraylength (env, BA);
int i=0;
Changing even-numbered array elements
for (i=0; i < Len; i+=2)
Pba[i] = Jni_false;
(*env)->releasebooleanarrayelements (env, BA, PBA, 0);
}

Pointers to Boolean arrays can be obtained using getbooleanarrayelements.
The size of the array can be obtained using the Getarraylength method. Use the Releasebooleanarrayelements method to release the array. You can now read and modify the values of the array elements. The Jsize declaration is equivalent to Jint (to see its definition, see the jni.h header file under the Include directory of the JDK).

Example 5--Passing a Java String array

This example shows how to access an array of non-basic objects, using the most common non-basic type, Java String. String arrays are passed to local methods, and local methods simply display them to the console.
The following methods are added to the Mynative class definition:

  public static void Showstrings (string[] sa)
{
SHOWSTRINGS0 (SA);
}
private static void ShowStrings0 (string[] sa);

and main add two lines to the method to test:

  string[] sa = new string[] {"Hello,", "world!", "JNI", "is", "Fun."};
Mynative.showstrings (SA);

The local method accesses each element individually, with the implementation shown below.

jniexport void Jnicall Java_mynative_showstrings0
(JNIEnv *env, Jclass cls, Jobjectarray sa)
{
int len = (*env)->getarraylength (env, SA);
int i=0;
for (i=0; i < Len; i++)
{
Jobject obj = (*env)->getobjectarrayelement (env, SA, i);
jstring str = (jstring) obj;
Const char* SZSTR = (*env)->getstringutfchars (env, str, 0);
printf ("%s", szstr);
(*env)->releasestringutfchars (env, str, SZSTR);
}
printf ("\ n");
}

Array elements can be accessed through getobjectarrayelement.
In this case, we know that the return value is the jstring type, so it is safe to convert it from the Jobject type to the jstring type. The string is printed by the method discussed previously. For information about working with Java strings in Windows, see a paper titled NLS Strings and JNI.

Example 6--Returning a Java String array

The last example shows how to create a string array in local code and return it to the Java caller. The following methods are added in Mynative.java:

  public static string[] Getstrings ()
{
return GetStrings0 ();
}
private static native string[] GetStrings0 ();

Change main so that showStrings the getStrings output is displayed:

  Mynative.showstrings (Mynative.getstrings ());

The implemented local method returns five strings.

Jniexport Jobjectarray Jnicall Java_mynative_getstrings0
(JNIEnv *env, Jclass CLS)
{
Jstring str;
Jobjectarray args = 0;
Jsize len = 5;
char* sa[] = {"Hello,", "world!", "JNI", "is", "Fun"};
int i=0;
args = (*env)->newobjectarray (env, Len, (*env)->findclass (env, "java/lang/string"), 0);
for (i=0; i < Len; i++)
{
str = (*env)->newstringutf (env, sa[i]);
(*env)->setobjectarrayelement (env, args, I, str);
}
return args;
}

The string array is created by calling Newobjectarray and passing the string class and array length two parameters. Java String is created using NEWSTRINGUTF. The String element is stored in an array using Setobjectarrayelement.



Back to top of page

Debugging

Now that you have created a local DLL for your application, keep the following points in mind when debugging. If you use the Java debugger Java_g.exe, you also need to create a "debug" version of the DLL. This simply means that you must create a version of the DLL with the same name but with a _g suffix. In the case of MyNative.dll, using Java_g.exe requires a Mynative_g.dll file in the path specified in the Windows path environment. In most cases, this DLL can be renamed or copied to the file whose name is prefixed with _g.

The Java debugger now does not allow you to enter local code, but you can debug the local method using a C debugger outside of the Java environment, such as Microsoft Visual C + +. First, import the source file into a project.
Adjust the compilation settings to include the includes directory at compile time:

C:\jdk1.1.6\include;c:\jdk1.1.6\include\win32  

Set the configuration to compile the DLL in debug mode. Under Debug in Project Settings, set the executable file to Java.exe (or java_g.exe, but make sure you have generated a _g.dll file). The program parameters include the class name that contains Main. If a breakpoint is set in a DLL, execution stops at the appropriate place when the local method is called.

Here are the steps to set up a Visual C + + 6.0 project to debug a local method.

    1. Create a Win32 DLL project in Visual C + + and Add. C and. h files to this project.




    • Set the JDK's include directory under Options settings in the Tools drop-down menu. The following dialog box shows these directories.


    • Select Build MyNative.dll under the Build drop-down menu to build this project. Make sure that the active configuration of the project is set to debug (this is usually the default).
    • Under Project Settings, set the Debug tab to invoke the appropriate Java interpreter, as follows:


When executing this program, the message "No debug information found in Java.exe" is ignored. When the local method is called, any breakpoints set in the C code will stop the execution of the Java program where appropriate.

Other information

JNI Methods and C + +

The above examples show how to use the JNI method in a C source file. If you are using C + +, format the corresponding method from:

(*env)->jnimethod (env, ...);  

Change to:

Env->jnimethod (...);  

In C + +, the JNI function is considered a member method of the JNIEnv class.

string and country language support

The techniques used in this article use the UTF method to convert strings. These methods are used only for convenience and cannot be used if the application requires national language support (NLS). For the correct method of handling Java strings in Windows and NLS environments, please refer to a paper titled NLS Strings and JNI.

Summary

The examples provided in this article illustrate how to implement local methods with the most commonly used data classes, such as Jint and Jstring, and discuss several Windows-specific issues, such as displaying strings. The examples provided in this article do not include all jni,jni and other parameter types, such as Jfloat, Jdouble, Jshort, Jbyte, and Jfieldid, as well as methods for handling these types. For more information on this topic, see the Java local interface specification provided by Sun Microsystems.

Implementing Java Call DLLs in Windows (reprint)

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: info-contact@alibabacloud.com 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.