Parameter passing of C + + and Java in JNI (JNI parameter passing)

There are many basic methods and processes of how to use JNI on the Internet. If you don't know much about JNI, what JNI does, and how to establish a basic JNI program, you may refer to the following articles:
The simplest example of using VC++6.0 to realize JNI 
HelloWorld of JNI introductory tutorial
SUN JNI Tutorial

In the examples of these materials, most of them just input some simple parameters and obtain no parameters. In the actual use process, it is often necessary to process and convert the parameters. Can be recognized by C/C + + programs. For example, we have a structure (structure) DiskInfo in C + +, which needs to pass a parameter similar to DiskInfo *pDiskInfo. How can parameters similar to C + + be passed to Java? Let's discuss the conversion of some common parameters from C + + to Java methods:

Define Native Java classes:

If you are used to using JNI, you won't find it difficult. Since native methods are implemented by other languages, they have no function body in Java. However, all local code must be declared with local keywords and become members of Java classes. Suppose we have such a structure in C + +, which is used to describe the hard disk information:

 // Hard disk information
 struct    {
     char  name[ 256 ];
     int  serial;
} DiskInfo;

Then we need to define a class in Java to match it. The declaration can be written as follows:

 class  DiskInfo  {
     // name
      public  String name;

     // serial number
      public   int  serial;
}

In this class, some Native local methods are declared to test the transfer of method parameters. Some functions are defined to transfer structure or structure array. The specific definitions are as follows:

 /* ***************** Define local methods******************* */
     // Enter common numeric types (Boolean,Byte,Char,Short,Int,Float,Double)
      public  native  void  displayParms(String showText,  int  i, boolean bl);

     // Call a static method
      public  native  int  add( int  a,  int  b);

     // Enter an array
      public  native  void  setArray(boolean[] blList);

     // Returns an array of strings
      public  native String[] getStringArray();

     // Returns a structure
      public  native DiskInfo getStruct();

     // Returns an array of structures
      public  native DiskInfo[] getStructArray();

Compile and generate C/C + + header files

After defining the Java class, the next step is to write local code. The local method symbol provides a header file that meets the Convention, using Java tool Javah You can easily create it without having to create it manually. If you use the javah command on the Java class file, a corresponding C/C + + header file will be generated for you.
1. Enter the work path under the console. The path of the project is E:\work\java\workspace\JavaJni.
2. Run the javah command: javah -classpath E:\work\java\workspace\JavaJni com.sundy.jnidemo ChangeMethodFromJni
The C/C + + header file generated in this article is named: com_sundy_jnidemo_ChangeMethodFromJni.h  

Implementing local methods in C/C + +

After generating the C/C + + header file, you need to write the local method corresponding to the header file. Note: the first parameter of all local methods points to the JNIEnv structure. This structure is used to call JNI functions. The meaning of the second parameter jclass depends on whether the method is static or Instance. In the former, jclass represents the reference of a class object, while the latter is the reference of the object to which the called method belongs.

The return value and parameter types are mapped to local C/C + + types according to the equivalence convention, as shown in table JNI type mapping. Some types can be used directly in local code, while others can only call operations through JNI.

Table A

Java   typeLocal typedescribe
booleanjbooleanC/C++8-bit integer
bytejbyteC/C + + signed 8-bit integer
charjcharC/C + + unsigned 16 bit integer
shortjshortC/C + + signed 16 bit integer
intjintC/C + + signed 32-bit integer
longjlongC/C + + signed 64 bit integer e
floatjfloatC/C++32-bit floating point
doublejdoubleC/C++64 bit floating point
ObjectjobjectAny Java object, or no object of corresponding Java type
ClassjclassClass object
StringjstringString object
Object[]jobjectArrayArray of any object
boolean[]jbooleanArrayBoolean array
byte[]jbyteArrayBit array
char[]jcharArrayCharacter array
short[]jshortArrayShort integer array
int[]jintArrayinteger array
long[]jlongArrayLong integer array
float[]jfloatArrayFloating point array
double[]jdoubleArrayDouble floating point array

※       JNI type mapping

Use array:

JNI operates Java arrays through the functions provided by JNIEnv. It provides two functions: one is to operate on a simple array of Java, and the other is to operate on an array of object types.

For speed reasons, arrays of simple types are exposed to local code as pointers to local types. Therefore, they can be accessed as regular arrays. This pointer is a pointer to the actual Java array or a copy of the Java array. In addition, the array is arranged to match the local type.

In order to access arrays of java simple types, you need to use the GetXXXArrayElements function (see table B). XXX represents the type of array. This function takes the Java array as a parameter and returns a pointer to the array of the corresponding local type.

Table B

functionJava   Array typeLocal type
GetBooleanArrayElementsjbooleanArrayjboolean
GetByteArrayElementsjbyteArrayjbyte
GetCharArrayElementsjcharArrayjchar
GetShortArrayElementsjshortArrayjshort
GetIntArrayElementsjintArrayjint
GetLongArrayElementsjlongArrayjlong
GetFloatArrayElementsjfloatArrayjfloat
GetDoubleArrayElementsjdoubleArrayjdouble

※ JNI array access function

After you access the array, make sure to call the corresponding ReleaseXXXArrayElements function. The parameters are the pointers returned by the corresponding Java array and GetXXXArrayElements. If necessary, this release function will copy any changes you make (so that they are reflected into the Java array), and then release all related resources.

In order to use the array of java objects, you must use the GetObjectArrayElement function and SetObjectArrayElement function to get and set the elements of the array respectively. The GetArrayLength function returns the length of the array.

Use object

Another feature provided by JNI is the use of Java objects in native code. By using appropriate JNI functions, you can create Java objects, get, set static and instance fields, and call static and instance functions. JNI identifies domains and methods by ID. the ID of a domain or method is a necessary parameter of any function dealing with domains and methods.

Table C lists the JNI functions used to get the fields and methods of static and instance. Each function accepts (as a parameter) classes of fields or methods, their names, symbols, and their corresponding returned jfield ID or jmethodids.

Table C

functiondescribe
GetFieldIDGet the domain ID of an instance
GetStaticFieldIDGet the ID of a static domain
GetMethodIDGet the ID of the method of an instance
GetStaticMethodIDGet the ID of a static method

※ functions of fields and methods

If you have an instance of a class, it can be obtained through the method GetObjectClass, or if you do not have an instance of this class, it can be obtained through FindClass. The symbol is a string returned from the field type or method parameter, as shown in table D.

Table D

Java   typeSymbol
booleanZ
byteB
charC
shortS
intI
longL
floatF
doubleD
voidV
objects objectLfully-qualified-class-name;L class name
Arrays array[array type]
methods method(argument types) return type

※ symbols for determining fields and methods

Let's take a look at how to use arrays and objects to obtain DiskInfo class objects in Java from in C + + and return a DiskInfo array:

//Returns a structure array and a structure array of hard disk information
JNIEXPORT jobjectArray JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_getStructArray
(JNIEnv *env, jobject _obj)
{
    //Declare an object array 
    jobjectArray args = 0;
    
    //Array size
    jsize        len = 5;

    //Get the class to which the object belongs, usually ava/lang/Object
    jclass objClass = (env)->FindClass("java/lang/Object");

    //New object array
    args = (env)->NewObjectArray(len, objClass, 0);

    /* The following is to get the variables in the corresponding instance class in Java*/

    //Get instance classes in Java
    jclass objectClass = (env)->FindClass("com/sundy/jnidemo/DiskInfo");
    
    //Get the definition of each variable in the class
    //name
    jfieldID str = (env)->GetFieldID(objectClass,"name","Ljava/lang/String;");
    //serial number
    jfieldID ival = (env)->GetFieldID(objectClass,"serial","I");

    //Pay values to the variables of each instance, and add the instance as an object to the object array
    for(int  i=0; i < len; i++ )
    {
        //Value the variables of each instance
        jstring jstr = WindowsTojstring(env,"My disk name is D:");
        //(env)->SetObjectField(_obj,str,(env)->NewStringUTF("my name is D:"));
        (env)->SetObjectField(_obj,str,jstr);
        (env)->SetShortField(_obj,ival,10);

        //Add to objcet array
        (env)->SetObjectArrayElement(args, i, _obj);
    }
    //Return object array
    return args;

 }

The implementation codes of all C/C + + methods are as follows:

// JniManage.cpp: defines the entry point for the DLL application.
//
package com.sundy.jnidemo;
#include "stdafx.h"

#include <stdio.h>
#include <math.h>
#include "jni.h"
#include "jni_md.h"

#include "./head/Base.h"
#include "head/wmi.h"
#include "head/com_sundy_jnidemo_ChangeMethodFromJni.h" / / generated through javah – jni javactransfer
#include <stdio.h>
#include "stdlib.h"
#include "string.h"

#pragma comment (lib,"BaseInfo.lib")
#pragma comment (lib,"jvm.lib")
//Hard disk information
struct  {
    char name[256];
    int serial;
}DiskInfo;
/*BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
                     )
{
    LPTSTR  strName = new CHAR[256] ;
    (*GetHostName)(strName);
    printf("%s\n",strName);
    delete [] strName;

    return TRUE;
}*/
//Convert jstring type to windows type
char* jstringToWindows( JNIEnv *env, jstring jstr );
//Convert windows type to jstring type
jstring WindowsTojstring( JNIEnv* env, char* str );

//Main function
BOOL WINAPI DllMain(HANDLE hHandle, DWORD dwReason, LPVOID lpReserved)
{
    return TRUE;
}
//Enter commonly used numeric types: Boolean,Byte,Char,Short,Int,Float,Double
JNIEXPORT void JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_displayParms
(JNIEnv *env, jobject obj, jstring s, jint i, jboolean b)
{
    const char* szStr = (env)->GetStringUTFChars(s, 0 );
    printf( "String = [%s]\n", szStr );
    printf( "int = %d\n", i );
    printf( "boolean = %s\n", (b==JNI_TRUE ? "true" : "false") );
    (env)->ReleaseStringUTFChars(s, szStr );
}

//Call a static method with only one simple type output
JNIEXPORT jint JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_add
(JNIEnv *env, jobject, jint a, jint b)
{
    int rtn = (int)(a + b);
    return (jint)rtn;
}

Enter an array,What you enter here is a Boolean Array of type
JNIEXPORT void JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_setArray
(JNIEnv *env, jobject, jbooleanArray ba)
{
    jboolean* pba = (env)->GetBooleanArrayElements(ba, 0 );
    jsize len = (env)->GetArrayLength(ba);
    int i=0;
    // change even array elements
    for( i=0; i < len; i+=2 )
    {
        pba[i] = JNI_FALSE;
        printf( "boolean = %s\n", (pba[i]==JNI_TRUE ? "true" : "false") );
    }
    (env)->ReleaseBooleanArrayElements(ba, pba, 0 );
}

Returns an array of strings
JNIEXPORT jobjectArray JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_getStringArray
(JNIEnv *env, jobject)
{
    jstring      str;
    jobjectArray args = 0;
    jsize        len = 5;
    char*        sa[] = { "Hello,", "world!", "JNI", "is", "fun" };
    int          i=0;
    args = (env)->NewObjectArray(len,(env)->FindClass("java/lang/String"),0);
    for( i=0; i < len; i++ )
    {
        str = (env)->NewStringUTF(sa[i] );
        (env)->SetObjectArrayElement(args, i, str);
    }
    return args;
}

//Returns a structure, where a simple structure type of hard disk information is returned
JNIEXPORT jobject JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_getStruct
(JNIEnv *env, jobject obj)
{
    /* The following is to get the variables in the corresponding instance class in Java*/

    //Get instance classes in Java
    jclass objectClass = (env)->FindClass("com/sundy/jnidemo/DiskInfo");

    //Get the definition of each variable in the class
    //name
    jfieldID str = (env)->GetFieldID(objectClass,"name","Ljava/lang/String;");
    //serial number
    jfieldID ival = (env)->GetFieldID(objectClass,"serial","I");


    //Value the variables of each instance
    (env)->SetObjectField(obj,str,(env)->NewStringUTF("my name is D:"));
    (env)->SetShortField(obj,ival,10);
    
    return obj;
}

//Returns a structure array and a structure array of hard disk information
JNIEXPORT jobjectArray JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_getStructArray
(JNIEnv *env, jobject _obj)
{
    //Declare an object array 
    jobjectArray args = 0;
    
    //Array size
    jsize        len = 5;

    //Get the class to which the object belongs, usually ava/lang/Object
    jclass objClass = (env)->FindClass("java/lang/Object");

    //New object array
    args = (env)->NewObjectArray(len, objClass, 0);

    /* The following is to get the variables in the corresponding instance class in Java*/

    //Get instance classes in Java
    jclass objectClass = (env)->FindClass("com/sundy/jnidemo/DiskInfo");
    
    //Get the definition of each variable in the class
    //name
    jfieldID str = (env)->GetFieldID(objectClass,"name","Ljava/lang/String;");
    //serial number
    jfieldID ival = (env)->GetFieldID(objectClass,"serial","I");

    //Pay values to the variables of each instance, and add the instance as an object to the object array
    for(int  i=0; i < len; i++ )
    {
        //Value the variables of each instance
        jstring jstr = WindowsTojstring(env,"My disk name is D:");
        //(env)->SetObjectField(_obj,str,(env)->NewStringUTF("my name is D:"));
        (env)->SetObjectField(_obj,str,jstr);
        (env)->SetShortField(_obj,ival,10);

        //Add to objcet array
        (env)->SetObjectArrayElement(args, i, _obj);
    }
    //Return object array
    return args;

 }

//Convert jstring type to windows type
char* jstringToWindows( JNIEnv  *env, jstring jstr )
{
    int length = (env)->GetStringLength(jstr );
    const jchar* jcstr = (env)->GetStringChars(jstr, 0 );
    char* rtn = (char*)malloc( length*2+1 );
    int size = 0;
    size = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)jcstr, length, rtn,(length*2+1), NULL, NULL );
    if( size <= 0 )
        return NULL;
    (env)->ReleaseStringChars(jstr, jcstr );
    rtn[size] = 0;
    return rtn;
}
//Convert windows type to jstring type
jstring WindowsTojstring( JNIEnv* env, char* str )
{
    jstring rtn = 0;
    int slen = strlen(str);
    unsigned short * buffer = 0;
    if( slen == 0 )
        rtn = (env)->NewStringUTF(str ); 
    else
    {
        int length = MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, NULL, 0 );
        buffer = (unsigned short *)malloc( length*2 + 1 );
        if( MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, (LPWSTR)buffer, length ) >0 )
            rtn = (env)->NewString(  (jchar*)buffer, length );
    }
    if( buffer )
        free( buffer );
    return rtn;
}

Java test native code
There's nothing more to say. Look at the code

//Main test procedure
    public static void main(String[] args) {
        ChangeMethodFromJni changeJni = new ChangeMethodFromJni();

        //Enter the commonly used numeric type (string int boolean)
        System.out
                .println("------------------Enter common numeric types(string int boolean)-----------");
        changeJni.displayParms("Hello World!", 100, true);

        //Call a static method
        System.out.println("------------------Call a static method-----------");
        int ret = changeJni.add(12, 20);
        System.out.println("The result is: " + String.valueOf(ret));

        //Enter an array
        System.out.println("------------------Enter an array-----------");
        boolean[] blList = new boolean[] { true, false, true };
        changeJni.setArray(blList);

        //Returns an array of strings
        System.out.println("------------------Returns an array of strings-----------");
        String[] strList = changeJni.getStringArray();
        for (int i = 0; i < strList.length; i++) {
            System.out.print(strList[i]);
        }
        System.out.println();

        System.out.println("------------------Returns a structure-----------");

        //Returns a structure
        DiskInfo disk = changeJni.getStruct();
        System.out.println("name:" + disk.name);
        System.out.println("Serial:" + disk.serial);

        //Returns an array of structures

        System.out.println("------------------Returns an array of structures -----------");
        DiskInfo[] diskList = changeJni.getStructArray();
        for (int i = 0; i < diskList.length; i++) {
            System.out.println("name:" + diskList[i].name);
            System.out.println("Serial:" + diskList[i].serial);
        }

    }

Original text: Parameter passing of C + + and Java in Jni - meditative dog blog - BlogJavahttp://www.blogjava.net/china-qd/archive/2006/04/29/44002.html

Other references: JNI parameter transfer - crossbow - blog Garden for basic data types, the corresponding parameters seen in the native layer interface have been converted to the data type of the native layer, but the name has been converted with typedef for good correspondence. Basic data type of JAVA: data type size rangehttps://www.cnblogs.com/zijianlu/archive/2012/11/01/2749390.html

 

Keywords: Java C++ JNI UE4

Added by timtom3 on Tue, 02 Nov 2021 13:54:02 +0200