Solved

JNI Interface - Passing Object Arrays

Posted on 1998-07-05
8
512 Views
Last Modified: 2013-11-23
I am utilizing native code (C++) that I must pass an array of user defined objects (not String -which I have seen examples of) to.  The native code also modifies this array of objects.  Is this possible and, if so, how do I do this?  Please send code examples.
0
Comment
Question by:kayak
  • 4
  • 4
8 Comments
 
LVL 5

Accepted Solution

by:
msmolyak earned 100 total points
Comment Utility
For the following java class:

class HelloWorld
{
      Object[] objectArray;

      public native void displayHelloWorld(Object[] array);

      static
      {
              System.loadLibrary("hello");
        }
      public static void main(String[] args)
      {
            Object[] newArray = new Object[2];
            newArray[0] = new String("String");
            newArray[1] = new Integer(42);
            new HelloWorld().displayHelloWorld(newArray);
      }
}

I generated a header file using "javah -kni HelloWorld" and got as a result:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */

#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloWorld
 * Method:    displayHelloWorld
 * Signature: ([Ljava/lang/Object;)V
 */
JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld
  (JNIEnv *, jobject, jobjectArray);

#ifdef __cplusplus
}
#endif
#endif

Thus your function definition has a parameter of type jobjectArray.

JNI has a set of functions (Array Operations) which allow you to find the array length, get each array element and assign to each element of the array. You can find them at http://java.sun.com/products/jdk/1.1/docs/guide/jni/spec/functions.doc.html#17314.

Let me know if you need more help.
0
 

Author Comment

by:kayak
Comment Utility
msmolyak,

I have tried passing the user defined object array in this way to the C++ Native code.  On the C++ side, I have tried the GetObjectArrayElement call to get the element and I have casted this to a pointer of the object type that I want.  However, when I use this object and issue a simple native accessor method I do not get the same values as I do on the Java side.  I have also tried creating the array of objects in the JNI C++ code using the NewObjectArray function, but I have had no luck doing this also.  If you could send me a complete example with the C++ code also, I would greatly appreciate it.

Basically, the test has a C++ object that contains several integers as members and some simple accessor functions.  In some cases an array of these objects are passed as an argument to the C++ member functions.  I have created the array of objects on the Java side either directly or by calling JNI method that uses the NewObjectArray function.  On the individual members of the array (individual objects) I have used the simple accessor methods to set and examine some values.  On the Java side this seems to work.  However, if I pass the entire array of object to the JNI C++ code and try to examine or set the contents of some of the individual objects, I don't get the same values (they are all 0) as what I had set in the objects on the Java side.  

Sorry for the long-winded explaination, but Please Help Me.
0
 
LVL 5

Expert Comment

by:msmolyak
Comment Utility
Could you instead send me some sample of your code (desirably compilable) which you are having problems with?
0
 

Author Comment

by:kayak
Comment Utility
msmolyak,

The code (stripped down) follows.  The first two are header files for the C++ files.  CPos.h has the interface spec for a small class CPosition.  CPosition.cpp has the code for this class.  Just for similarity sake with the actual code, I made this a library called Position.lib (static lib).  The CPosition.h header file is the JNI created header.  CPositionJNI.cpp has the JNI interface code that also contains a method called arrayPass which tries to access the passed in array from Java.  The CPosition.java code contains a main to utilize the various methods.

After the Position.lib has been created (use MSDevStudio), use the following compile the cpp JNI code:
cl -LD Position.lib CPositionJNI.cpp -FeCPositionJNI.dll


//////////////////////
CPos.h
//////////////////////
class CPosition
{

   public:
      CPosition();
      ~CPosition();
      void SetX(int x);
      void SetY(int y);
      int GetX();
      int GetY();      

      //fixed coordinate (in pixels on the image)
      int    Nfeatures;
      int    xf[5],yf[5];
      int    X,Y,S,W,H,XC,YC;
      float  theta;
};



//////////////////////////////
CPosition.cpp
//////////////////////////////
class CPosition
{

   public:
      CPosition();
      ~CPosition();
      void SetX(int x);
      void SetY(int y);
      int GetX();
      int GetY();      

      //fixed coordinate (in pixels on the image)
      int    Nfeatures;
      int    xf[5],yf[5];
      int    X,Y,S,W,H,XC,YC;
      float  theta;
};


     CPosition::CPosition()
     {
     }    

     CPosition::~CPosition()
     {
     }

     void CPosition::SetX(int x)
     {
       X=x;
     }

     void CPosition::SetY(int y)
     {
       Y=y;
     }

     int CPosition::GetX()
     {
        return(X);
     }

     int CPosition::GetY()
     {
        return(Y);
     }



/////////////////////////////////
CPosition.h
////////////////////////////////////
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class CPosition */

#ifndef _Included_CPosition
#define _Included_CPosition
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     CPosition
 * Method:    initialize0
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_CPosition_initialize0
  (JNIEnv *, jobject);

/*
 * Class:     CPosition
 * Method:    finalize0
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_CPosition_finalize0
  (JNIEnv *, jobject);

/*
 * Class:     CPosition
 * Method:    newPositionArray0
 * Signature: (I)[LCPosition;
 */
JNIEXPORT jobjectArray JNICALL Java_CPosition_newPositionArray0
  (JNIEnv *, jclass, jint);

/*
 * Class:     CPosition
 * Method:    arrayPass0
 * Signature: ([LCPosition;)V
 */
JNIEXPORT void JNICALL Java_CPosition_arrayPass0
  (JNIEnv *, jobject, jobjectArray);

/*
 * Class:     CPosition
 * Method:    getX0
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_CPosition_getX0
  (JNIEnv *, jobject);

/*
 * Class:     CPosition
 * Method:    getY0
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_CPosition_getY0
  (JNIEnv *, jobject);

/*
 * Class:     CPosition
 * Method:    setX0
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_CPosition_setX0
  (JNIEnv *, jobject, jint);

/*
 * Class:     CPosition
 * Method:    setY0
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_CPosition_setY0
  (JNIEnv *, jobject, jint);

#ifdef __cplusplus
}
#endif
#endif



/////////////////////////////////////
CPositionJNI.cpp
////////////////////////////////////
  #include "COMDEF.h"
  #include "CPosition.h"
  #include "CPos.h"

  void DllMain()
  {
  }

  /*********************************************************************
  * Retrieves the pointer to the C++ peer object from the Java object.
  *********************************************************************/
  jlong
  getCID (
    JNIEnv*  env,
    jobject  anObj )
  {
    jclass  clazz = env->GetObjectClass ( anObj );
    jfieldID  fid = env->GetFieldID ( clazz, "peerPtr", "J" );
    return env->GetLongField ( anObj, fid );
  }

  /*********************************************************************
  * Sets the pointer to the C++ peer object in the Java object.
  *********************************************************************/
  void
  setCID (
    JNIEnv*  env,
    jobject  anObj,
    jlong    val )

  {
    jclass  clazz = env->GetObjectClass ( anObj );
    jfieldID  fid = env->GetFieldID ( clazz, "peerPtr", "J" );
    env->SetLongField ( anObj, fid, val );
  }

  //////////////////////////////////////////////////////////////////////
  // Initialization and finalization methods
  //////////////////////////////////////////////////////////////////////

  /*********************************************************************
  * Called when a Java object is first constructed.
  * Sets the pointer to the C++ peer object in the Java object.
  *********************************************************************/
  JNIEXPORT void JNICALL
  Java_CPosition_initialize0
  (JNIEnv    *env,
   jobject   thisObject)
  {
    setCID ( env, thisObject, ( jlong ) new CPosition ( ) );
  }

  /*********************************************************************
  * Called implicitly by the Java garbage collector to free the memory
  * allocated to the C++ peer object during the initialization routine.
  *********************************************************************/
  JNIEXPORT void JNICALL Java_CPosition_finalize0
  (JNIEnv    *env,
   jobject   thisObject)
  {
    CPosition*  cPosition = ( CPosition* ) getCID ( env, thisObject );
    cPosition->~CPosition ( );
  }

  //////////////////////////////////////////////////////////////////////
  // Computational methods
  //////////////////////////////////////////////////////////////////////
  JNIEXPORT jobjectArray JNICALL Java_CPosition_newPositionArray0
  (JNIEnv     *env,
   jclass     thisObject,
   jint       sz)
  {
    jclass   clazz;
    clazz = env->FindClass("CPosition");
    jobjectArray newArr;
    newArr = env->NewObjectArray(sz, clazz, NULL);

    return newArr;
  }

  //////////////////////////////////////////////////////////////////////
  // Mutator and accessor methods
  //////////////////////////////////////////////////////////////////////
  JNIEXPORT void JNICALL Java_CPosition_arrayPass0
  (JNIEnv          *env,
   jobject         thisObject,
   jobjectArray    position)
  {
    jobject       tempPosition;
    jobject       *tempPointer;
    CPosition     *tempCPointer;
    CPosition     tempCPosition;

    // Copy each Java array element into the C++ array
    jint positionSize = env->GetArrayLength(position);

    printf("C++ Array size is: %d\n", positionSize);
    fflush(stdout);

    for (int i=0; i<positionSize; i++)
    {
      tempPosition = env->GetObjectArrayElement(position, i);
      tempCPointer = (CPosition *)&tempPosition; // Could be moved
                                                 // out of the loop

      printf("C++ Pos0X is %d, Pos0Y is %d\n", tempCPointer->GetX(),
        tempCPointer->GetY());
      fflush(stdout);
     }

  }

  JNIEXPORT jint JNICALL Java_CPosition_getX0
  (JNIEnv      *env,
   jobject     thisObject)
  {
    CPosition*  cPosition = ( CPosition* ) getCID ( env, thisObject );
    return cPosition->GetX();
  }

  JNIEXPORT jint JNICALL Java_CPosition_getY0
  (JNIEnv      *env,
   jobject     thisObject)
  {
    CPosition*  cPosition = ( CPosition* ) getCID ( env, thisObject );
    return cPosition->GetY();
  }

  JNIEXPORT void JNICALL Java_CPosition_setX0
  (JNIEnv      *env,
   jobject     thisObject,
   jint        x)
  {
    CPosition*  cPosition = ( CPosition* ) getCID ( env, thisObject );
    cPosition->SetX(x);
  }

  JNIEXPORT void JNICALL Java_CPosition_setY0
  (JNIEnv      *env,
   jobject     thisObject,
   jint        y)
  {
    CPosition*  cPosition = ( CPosition* ) getCID ( env, thisObject );
    cPosition->SetY(y);
  }

//////////////////////////////////
CPosition.java
//////////////////////////////////
public class CPosition
  //////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////
  {

    /** Note that the pointer to the C++ peer object is transient. */
    /** This is just in case the object is ever made Serializable. */
    private transient long  peerPtr = 0;

    //////////////////////////////////////////////////////////////////////
    // Static initializer method.
    //////////////////////////////////////////////////////////////////////

    static
    //////////////////////////////////////////////////////////////////////
    {
       Runtime.runFinalizersOnExit ( true );

       // What if this is already loaded by another class?
       // Will it throw an exception?
       System.loadLibrary ( "CPositionJNI" );
    }

     /* ********************************************************************
     * Test method.
     ******************************************************************** */
     public static void  main ( String [ ]  args )
     //////////////////////////////////////////////////////////////////////
     {

       System.out.println("Creating master object");
       System.out.flush();

       CPosition master = new CPosition();

       System.out.println("Creating position object");
       System.out.flush();
       
//       CPosition[] position = newPositionArray0(10);
       CPosition position[] = new CPosition[10];

       // allocate space for each array element
       for (int i=0; i<position.length; i++)
       {
          position[i] = new CPosition();
       }

       System.out.println("Initializing values.");
       System.out.flush();

       position[0].SetX(4);
       position[0].SetY(5);
       position[1].SetX(6);
       position[1].SetY(7);
       position[2].SetX(8);
       position[2].SetY(9);
       position[3].SetX(10);
       position[3].SetY(11);
       position[4].SetX(12);
       position[4].SetY(13);

       System.out.println("Done Initializing.");
       System.out.flush();

       System.out.println(
"Pos0 = " + position[0].GetX() + ", " + position[0].GetY() + "\n" +
"Pos1 = " + position[1].GetX() + ", " + position[1].GetY() + "\n" +
"Pos2 = " + position[2].GetX() + ", " + position[2].GetY() + "\n" +
"Pos3 = " + position[3].GetX() + ", " + position[3].GetY() + "\n" +
"Pos4 = " + position[4].GetX() + ", " + position[4].GetY() + "\n");

       master.ArrayPass( position );

     }

     //////////////////////////////////////////////////////////////////////
     // Constructor methods
     //////////////////////////////////////////////////////////////////////

     /*********************************************************************
     * Initializes the C++ peer and saves the pointer.
     * The instance initializer is automatically called whenever a new
     * object is constructed.
     *********************************************************************/
     //////////////////////////////////////////////////////////////////////
     {
       initialize0 ( );
     }

     /*********************************************************************
     * This constructor simply calls the superclass constructor.
     * It is included as a reminder that this is the default behavior
     * if this is not explicitly implemented as shown below.
     *********************************************************************/
     public  CPosition ( )
     //////////////////////////////////////////////////////////////////////
     {
       super ( );
     }

     /*********************************************************************
     * Frees the memory for the C++ peer object.  Called by the Java
     * garbage collector automatically; do not explicitly call this method.
     *********************************************************************/
     public void  finalize ( ) throws Throwable
     //////////////////////////////////////////////////////////////////////
     {
       finalize0 ( );
       // Always call the superclass finalizer as the last step.
       super.finalize ( );
     }

    public int GetX( )
    ///////////////////////////////////////////////////////////////////////
    {
      return getX0( );
    }

    public int GetY( )
    {
      return getY0( );
    }
      
    public void SetX(
        int x)
    {
      setX0 (x);
    }
   
    public void SetY(
        int y)
    {
      setY0(y);
    }

    public void ArrayPass(
        CPosition[] position)
    {
      arrayPass0( position);
    }

    //////////////////////////////////////////////////////////////////////
    // Private native methods
    //////////////////////////////////////////////////////////////////////

    private native void  initialize0 ( );
    private native void  finalize0 ( );
    private native static CPosition[] newPositionArray0(int sz);

    private native void arrayPass0( CPosition[] position);

    private native int getX0();
    private native int getY0();
    private native void setX0(int x);
    private native void setY0(int y);

}


0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 5

Expert Comment

by:msmolyak
Comment Utility
Before I look at the C++ code could you tell me what this piece of java code does:

 /*********************************************************************
            * Initializes the C++ peer and saves the pointer.
            * The instance initializer is automatically called whenever a new
            * object is constructed.
            *********************************************************************/
            //////////////////////////////////////////////////////////////////////
            {
              initialize0 ( );
            }
0
 

Author Comment

by:kayak
Comment Utility
msmolyak,

That piece of code is executed whenever a CPosition object is created in the Java code.  This code then "calls" the C++ code routine with the same name which saves a pointer to the object.

Thanks,

Russ
0
 

Author Comment

by:kayak
Comment Utility
msmolyak,

I actually figured it out on my own.  Thanks so much for your efforts in helping solve this problem.  The following is the line I added in ArrayPass method in the C++ side.

    for (int i=0; i<positionSize; i++)
    {
      tempPosition = env->GetObjectArrayElement(position, i);
//Removed: tempCPointer = (CPosition *)&tempPosition;

ADDED: tempCPointer = ( CPosition* ) getCID ( env, tempPosition );


0
 
LVL 5

Expert Comment

by:msmolyak
Comment Utility
Glad you found it. Sorry I was not of more help.
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

An old method to applying the Singleton pattern in your Java code is to check if a static instance, defined in the same class that needs to be instantiated once and only once, is null and then create a new instance; otherwise, the pre-existing insta…
Java had always been an easily readable and understandable language.  Some relatively recent changes in the language seem to be changing this pretty fast, and anyone that had not seen any Java code for the last 5 years will possibly have issues unde…
Video by: Michael
Viewers learn about how to reduce the potential repetitiveness of coding in main by developing methods to perform specific tasks for their program. Additionally, objects are introduced for the purpose of learning how to call methods in Java. Define …
This video teaches viewers about errors in exception handling.

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

17 Experts available now in Live!

Get 1:1 Help Now