TweetFollow Us on Twitter

Calling C Code from Java

Volume Number: 13 (1997)
Issue Number: 9
Column Tag: Java Workshop

Calling C Code from Java

by Andrew Downs

Using native code with Java applications and the Sun JDK

Introduction

This article shows how to access C code (also called native code) from within Java code. The C code exists as a PowerPC shared library containing several Process Manager calls. The functions in the shared library are used to get and store process information. A Java front-end calls the shared library at specified intervals to retrieve and display the process information. Timing for these calls is accomplished using a thread. The front-end also includes a preferences dialog with a pseudo-custom control written in Java. This program runs as a Java application, not an applet.

This article assumes you are familiar with both Java and the Macintosh Toolbox. The Java portion of this program was developed using the Sun Java Development Kit (JDK) version 1.0.2. The native code is written in C. CodeWarrior 9 is used to build a PowerPC shared library from the C code.

Why Native Code?

One of Java's strengths is that it often insulates the programmer from the specifics of the users' platform. Sometimes, however, you need access to information that cannot be retrieved using one of the existing Java packages.

Fortunately, Java allows you to call non-Java code, usually referred to as native code. Using the Sun JDK v1.0.2 on the Macintosh, this native code must exist as a PowerPC shared library. If you have access to one of the non-Java integrated development environments, creating such a library is available as a project option. Refer to your development environment information for details. This article will use CodeWarrior to create a shared library. A sample CodeWarrior project for this purpose is also included with the JDK.

Briefly, here are the steps in the overall development process:

  1. Write and compile the Java code that makes the call to a native method. If the file is called <native>.java, the generated code will be contained in <native>.class.
  2. Drop the <native>class file onto the JavaH application (included with the JDK), which will generate the header and stub files.
  3. Move the <native>.h and <native>.stub files into the folder with your native code.
  4. Build the native library in CodeWarrior, using <native>.c as the source file.
  5. Move the native library (or an alias to it) to one of two places: the JavaSoft Folder in the Extensions folder, or the folder containing the Java front-end. (These locations are mapped into the classpath by Java Runner, so it can find any supporting code.)
  6. Write and compile the Java code for the front-end.
  7. Drop the .class file containing your application's main() method onto the Sun Java Runner application to run it.

Before we begin, let's take a closer look at shared libraries and what should be put in them.

What's in a Shared Library?

A shared library consists of functions that may be called by one or more applications (or other types of code, including other libraries). The shared library functions do not run as standalone code. Rather, the shared library provides its services to applications that know how to call its member functions. Shared library code exists in a separate file from the code fragment or fragments that comprise the application.

When creating a PowerPC shared library, the function code gets compiled into the data fork of the library file. The data fork acts as a container for the compiled code. (Multiple containers may exist in the data fork, but we won't deal with that issue here.) A code fragment resource (of type 'cfrg') with an ID of 0 (zero) is placed in the resource fork of the library file. This resource contains information about where in the data fork the library code begins, as well as what architecture the code adheres to (in this case, PowerPC).

The shared library must be loaded into memory before use. This may be done when the client application starts up, or as requested by the application. Our Java code relies on the latter approach: one of the classes contains a call to load and prepare the library at a specific time during program execution. The Process Manager (which handles the launching and running of applications) relies on the services of the Code Fragment Manager to prepare the library code for execution. (For more information on the Code Fragment Manager, refer to Inside Macintosh: PowerPC System Software.)

You will see that the source code for our shared library does not resemble a typical Macintosh application. It has no event loop, and does not initialize any of the Toolbox managers. Instead, the functions in our shared library rely on the client application to set up any managers that may be required.

The current version of the JDK does not contain any packages or classes that let you peek at the currently running processes on the system. That will be the purpose of our shared library: to obtain and provide process information to our Java front-end program.

Getting the process information into the shared library is easy. A function in the shared library calls the appropriate Process Manager routines, and saves the data they return.

We need a way to get this process information from the shared library to our Java program. We accomplish this using a Java class containing definitions of native methods. (The word "native" is a modifier in Java which may be used in method declarations. It indicates that the body of the method is defined elsewhere, and is not Java code.) These native methods are really calls to C functions in the shared library. We will treat them like accessor methods: each is responsible for one action, such as returning to the caller a specific piece of data. One of them instructs the shared library to read a new set of process information. Another returns the number of current processes. A third returns the partition size of a specific process, and so on.

Setting up the Shared Library

At this point, we know that we have to create a shared library containing C code, and that the functions in this library will be called using native methods in a Java class. We determined that the functions will return specific pieces of data. We can now design the shared library in more detail.

The Process Manager information we need includes the name, partition size, and current RAM in use by each process. Once we get the information, we store it in a (global) array of structs. Each struct contains the data for one process. To get at the data, we will walk through the array, checking each element in turn. We can setup the header file for our C code as follows:

Listing 1: ProcessWatcher.h

ProcessWatcher.h
The header file for ProcessWatcher.c.

/**************************
  ProcessWatcher.h
**************************/
//  Define some macros to make our code more readable.
#define  NIL                0L
#define  ONE_KBYTE_SIZE      1024
#define  DIFFERENCE_FACTOR    16
#define  MAX_NUM_RECORDS      20
#define  STRING_LENGTH        32

//  Define a struct that holds the information we want for
//  one process. Then create an array of these structs. 
struct appStruct
{
  Str32  processName;
  long    processSize;
  long    currentSize;
}  ;

struct    appStruct  gArray[ MAX_NUM_RECORDS ];

//  One global variable as an array counter.
short    gNumRecords;

//  Our function prototypes.
long    DoGetNumRecords( void );
long    DoGetCurrentSize( long i );
long    DoGetProcessSize( long i );
long    DoGetProcessNameLength( long i );
long    DoGetProcessNameChar( long i, long j );
void    DoGetProcessInfo( void );

The first five function prototypes listed above retrieve array data for the caller (in our program, the Java front-end). The last prototype corresponds to the function which actually calls the Process Manager.

Notice that all of the return values are long integers. This is because of the translation of Java primitive data types to Macintosh data types, which we will examine shortly.

Now we can look at the implementation of those functions. This is a partial listing of the contents of ProcessWatcher.c. We will view the remainder shortly.

Listing 2: ProcessWatcher.c

ProcessWatcher.c
Retrieve and save information about currently running processes.

/**************************
  DoGetNumRecords
**************************/
long    DoGetNumRecords( void )
{
  return( ( long )gNumRecords );
}

/**************************
  DoGetCurrentSize
**************************/
long    DoGetCurrentSize( long i )
{
  return( gArray[ i ].currentSize );
}
/**************************
  DoGetProcessSize
**************************/
long    DoGetProcessSize( long i )
{
  return( gArray[ i ].processSize );
}

/**************************
  DoGetProcessNameLength
**************************/
long    DoGetProcessNameLength( long i )
{
  return( gArray[ i ].processName[ 0 ] );
}

/**************************
  DoGetProcessNameChar
**************************/
long    DoGetProcessNameChar( long i, long j )
{
  return( gArray[ i ].processName[ j ] );
}

/**************************
  DoGetProcessInfo
**************************/
//  This routine walks through the current process list
//  and retrieves information we need.
void    DoGetProcessInfo( void )
{
  FSSpec              theSpec;
  OSErr              theError;
  ProcessInfoRec      theInfoRec;
  ProcessSerialNumber  thePSN;
  Str32              theName;
  
  //  Reset the array count.
  gNumRecords = 0;

  //  Setup the record for retrieving process info.
  theInfoRec.processInfoLength = sizeof( ProcessInfoRec );
  theInfoRec.processName = theName;
  theInfoRec.processAppSpec = &theSpec;
  
  thePSN.highLongOfPSN = NIL;
  thePSN.lowLongOfPSN = kNoProcess;

  //  Retrieve process info until no more processes are found.
  while ( GetNextProcess( &thePSN ) == noErr )
  {
    theError = GetProcessInformation( &thePSN, &theInfoRec );

    if ( theError != noErr )
      break;

    //  Save the process data into the global array. Start with the process name.
    BlockMove( ( Ptr )theInfoRec.processName, 
      ( Ptr )gArray[ gNumRecords ].processName, 
      STRING_LENGTH );

  //  Partition sizes are 16 bytes off when compared to "About This Macintosh".
  //  Current sizes are 15 bytes off when compared to "About This Macintosh".
  //  Adjust and store the resulting values so our display matches the system display.
    gArray[ gNumRecords ].processSize = 
      ( theInfoRec.processSize / ONE_KBYTE_SIZE ) 
      - DIFFERENCE_FACTOR;
    gArray[ gNumRecords ].currentSize = (
      ( theInfoRec.processSize - theInfoRec.processFreeMem ) 
      / ONE_KBYTE_SIZE ) - DIFFERENCE_FACTOR + 1;

    gNumRecords++;
    
    //  Bounds checking.
    if ( gNumRecords > MAX_NUM_RECORDS )
      break;
  }
}

This is a good start, so now we turn our attention to the Java class that contains the native methods. Remember, these are the Java calls to our C code.

We will abstract this portion of the design slightly to allow some flexibility. This means that the names of the native methods will not match the function names just described, although they have a similar purpose. Why is this? To allow us to make minor modifications to either piece without affecting the other. For instance, we can change the variable names in the C code, and the Java class still performs the same as before.

Let's take a closer look at the Java class containing the native methods.

Native Methods in a Java Class

To use the shared library, Java requires a class definition that both loads the library and calls the native methods within it. It also requires method declarations inserted in the calling class file matching the prototype for the called function. Notice that these declarations contain arguments but no body, ending in a semicolon (similar to a function prototype). The keyword 'native' tells the compiler that the code for these methods will be defined elsewhere.

Listing 3 shows the Java code for our native class (NProcessWatcher). The purpose of the static initializer is to ensure that the shared library gets loaded when this class gets loaded. (For details on the loading process, refer to Inside Macintosh: PowerPC System Software, and its discussion of the Code Fragment Manager.)

Listing 3: NProcessWatcher.java

NProcessWatcher.java
The class that will interface to our shared library.

public class NProcessWatcher
{
  //  Tell the VM to load our shared library.
  static
  {
    System.loadLibrary("ProcessWatcher Library");
  }
  
  //  Here are definitions for several simple native methods. Note that the data types 
  //  we specify here may not be the same in the corresponding header file, due to 
  //  size differences between Java's primitive types and the Macintosh's data types. 
  //  The stubs file should handle thetranslation of the arguments and return types 
  //  for us.
  
  //  This method invokes the Process Manager portion of the shared library.
  private native void nativeGetProcessInfo();
  
  //  The following methods retrieve various pieces of information
  //  about the current processes. 
  private native int nativeGetNumElements();
  
  private native int nativeGetCurrentSize( int i );
  
  private native int nativeGetProcessSize( int i );
  
  private native int nativeGetProcessNameLength( int i );
  
  private native char 
    nativeGetProcessNameChar( int i, int j );
  
  //  Our class constructor. It calls our native method
  //  that updates the Process Manager information.
  public NProcessWatcher()
  {
    this.nativeGetProcessInfo();
  }

  //  Update the Process Manager information on request.
  public void updateNow()
  {
    this.nativeGetProcessInfo();
  }

  //  Accessor methods used by our other Java classes to get process info. By hiding 
  //  the actual calls to the native methods, we can change the implementation 
  //  without affecting the classes calling in. 
  public int getNumElements()
  {
    return( this.nativeGetNumElements() );
  }

  public int getCurrentSize( int i )
  {
    return( this.nativeGetCurrentSize( i ) );
  }

  public int getProcessSize( int i )
  {
    return( this.nativeGetProcessSize( i ) );
  }

  public int getProcessNameLength( int i )
  {
    return( this.nativeGetProcessNameLength( i ) );
  }

  public char getProcessNameChar( int i, int j )
  {
    return( this.nativeGetProcessNameChar( i, j ) );
  }
}

With this class defined, we can now compile it. It does not rely on any other classes, so it will compile by itself using the Java Compiler in the JDK.

We still need a way to tie together this class and our shared library. Specifically, we need two additional files. One is a second header file, containing the prototypes for the Java calls into the C code. The other is a stubs file that can handle the translation of arguments and return values between our Java and C code. The JDK provides a tool that will handle this for us: JavaH.

The JavaH utility generates the header and stub files from the class file containing the native methods. After you compile NProcessWatcher.java, drop the resulting class file (NProcessWatcher.class) on the JavaH icon. This should create NProcessWatcher.h and NProcessWatcher.stubs in the same folder as JavaH.

Move these two files into the folder containing your CodeWarrior project before building the library. You should not modify these files. However, we must use of the information in the header file. If you open NProcessWatcher.h, you will see the native method declarations, rewritten as C function prototypes.

Listing 4: NProcessWatcher.h

NProcessWatcher.h
The JavaH generated header file for our native methods. This listing is
reformatted to fit the magazine page.

#include <native.h>

/* header for class NProcessWatcher */
#ifndef _Included_NProcessWatcher
#define _Included_NProcessWatcher
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ClassNProcessWatcher {
  char pad[1];  /* Padding for ANSI C */
} ClassNProcessWatcher;

HandleTo(NProcessWatcher);

extern void NProcessWatcher_nativeGetProcessInfo
              (struct HNProcessWatcher*);
extern long NProcessWatcher_nativeGetNumElements
              (struct HNProcessWatcher*);
extern long NProcessWatcher_nativeGetCurrentSize
              (struct HNProcessWatcher*, long);
extern long NProcessWatcher_nativeGetProcessSize
              (struct HNProcessWatcher*, long);
extern long NProcessWatcher_nativeGetProcessNameLength
              (struct HNProcessWatcher*, long);
extern /*unicode*/ long
              NProcessWatcher_nativeGetProcessNameChar
              (struct HNProcessWatcher*, long, long);
#ifdef __cplusplus
}
#endif

#endif /* _Included_NProcessWatcher */

Copy the six prototypes over to ProcessWatcher.c, and paste them near the top, after the #includes. Delete the word "extern" at the front of each one. Now we can fill in the bodies for these functions. Here is the revised version of ProcessWatcher.c:

Listing 2 revisited: ProcessWatcher.c

ProcessWatcher.c
Retrieve and save information about currently running processes.

//  Implementation of a native shared library for MacOS.
//  Adapted from the example provided with the JDK v1.0.2.

//  OS Headers
#include <CodeFragments.h>

//  JavaH generated Header
#include "NProcessWatcher.h"

//  Our library header.
#include "ProcessWatcher.h"

//  The first functions listed are the calls into our native library. They match the 
//  prototypes in the header file NProcessWatcher.h, which we included above.

//  The Java data types (int and char) expected as return types by NProcessWatcher.java 
//  map to the long int data type on the Macintosh. We didn't arbitrarily select this; it 
//  was generated by JavaH as part of NProcessWatcher.h

//  We have functions calling functions to get the process data.
//  This allows us to change the underlying implementation if necessary.

void NProcessWatcher_nativeGetProcessInfo
              (struct HNProcessWatcher* self)
{
  DoGetProcessInfo();
}

long NProcessWatcher_nativeGetNumElements
              (struct HNProcessWatcher* self)
{
  return( DoGetNumRecords() );
}

long NProcessWatcher_nativeGetCurrentSize
              (struct HNProcessWatcher* self, long i)
{
  return( DoGetCurrentSize( i ) );
}

long NProcessWatcher_nativeGetProcessSize
              (struct HNProcessWatcher* self, long i)
{
  return( DoGetProcessSize( i ) );
}

long NProcessWatcher_nativeGetProcessNameLength
              (struct HNProcessWatcher*, long i)
{
  return( DoGetProcessNameLength( i ) );
}

long NProcessWatcher_nativeGetProcessNameChar
              (struct HNProcessWatcher*, long i, long j)
{
  return( DoGetProcessNameChar( i, j ) );
}

/**************************
  DoGetNumRecords
**************************/

long    DoGetNumRecords( void )
{
  return( ( long )gNumRecords );
}

/**************************
  DoGetCurrentSize
**************************/

long    DoGetCurrentSize( long i )
{
  return( gArray[ i ].currentSize );
}

/**************************
  DoGetProcessSize
**************************/

long    DoGetProcessSize( long i )
{
  return( gArray[ i ].processSize );
}

/**************************
  DoGetProcessNameLength
**************************/

long    DoGetProcessNameLength( long i )
{
  return( gArray[ i ].processName[ 0 ] );
}

/**************************
  DoGetProcessNameChar
**************************/

long    DoGetProcessNameChar( long i, long j )
{
  return( gArray[ i ].processName[ j ] );
}

/**************************
  DoGetProcessInfo
**************************/

//  This routine walks through the current process list
//  and retrieves information we need.
void    DoGetProcessInfo( void )
{
  FSSpec              theSpec;
  OSErr              theError;
  ProcessInfoRec      theInfoRec;
  ProcessSerialNumber  thePSN;
  Str32              theName;
  
  //  Reset the array count.
  gNumRecords = 0;

  //  Setup the record for retrieving process info.
  theInfoRec.processInfoLength = sizeof( ProcessInfoRec );
  theInfoRec.processName = theName;
  theInfoRec.processAppSpec = &theSpec;
  
  thePSN.highLongOfPSN = NIL;
  thePSN.lowLongOfPSN = kNoProcess;

  //  Retrieve process info until no more processes are found.
  while ( GetNextProcess( &thePSN ) == noErr )
  {
    theError = GetProcessInformation( &thePSN, &theInfoRec );

    if ( theError != noErr )
      break;

    //  Save the process data into the global array. Start with the process name.
    BlockMove( ( Ptr )theInfoRec.processName, 
      ( Ptr )gArray[ gNumRecords ].processName, 
      STRING_LENGTH );

    //  Partition sizes are 16 bytes off when compared to "About..."
    //  Current sizes are 15 bytes off when compared to "About..."
    gArray[ gNumRecords ].processSize = 
      ( theInfoRec.processSize / ONE_KBYTE_SIZE ) 
      - DIFFERENCE_FACTOR;
    gArray[ gNumRecords ].currentSize = ( 
      ( theInfoRec.processSize - theInfoRec.processFreeMem ) 
      / ONE_KBYTE_SIZE ) - DIFFERENCE_FACTOR + 1;

    gNumRecords++;
    
    //  Bounds checking.
    if ( gNumRecords > MAX_NUM_RECORDS )
      break;
  }
}

Each prototype contains the class name, followed by an underscore, then the name of the native method from the class. The parameters include a reference to the calling class, and any arguments. The first function is declared as void, and simply calls another void function in this c file. The other functions accept parameters and return values. We specified Java integers in the original method declarations, and we see here that they were adjusted (by JavaH) to Macintosh long ints in the header file to correspond to the 32-bit data type.

These functions call other functions in this file to get process data. Those values are then returned to the caller (NProcessWatcher).

One more step is required before we attempt to make the shared library project. Make an alias to the file "Java Shared Library", which is located in the path System Folder:Extensions:JavaSoft Folder. Place the alias into the folder containing the shared library project.

The Project Definition

Figure 1 shows the CodeWarrior project definition.

Figure 1. The CodeWarrior project.

Here are several of the project preferences settings necessary to create the shared library:

PPC Project:
File Name:    ProcessWatcher Library
Creator:    Java
Type:      shlb

PPC PEF:
Fragment Name:  ProcessWatcher Library
Library Folder ID:  0

Our shared library is actually a code fragment. The Preferred Executable Format (PEF) dictates the layout of the generated fragment. The Fragment Name will be used by the Code Fragment Manager to identify and load the fragment. We don't use the Library Folder ID setting: it is for specifying the ID of an alias resource, which points to a directory containing other libraries that our fragment uses.

We also do not use the version numbers (also in the PPC PEF window), preferring to leave them at 0 (zero). If you create additional versions of the library, and do not maintain backward compatibility, you can use version numbers to specify which versions of other fragments may call in to (or be called by) this fragment.

There are no initialization or termination entry points defined for this project. As indicated in the CodeWarrior documentation, such functions could be used to setup or teardown the resources or memory needed by the fragment. There is no main entry point for a shared library, and so we do not specify one.

Figure 2 shows the files used in creating the native library. (A copy of the shared library is also shown.)

Figure 2. The files used to build the native library.

These are the contents of the files shown in Figure 2:

  • Java Shared Library: an alias to a file in the JavaSoft folder, required during the build.
  • NProcessWatcher.h: header for ProcessWatcher. Generated by JavaH.
  • NProcessWatcher.stubs: stubs for ProcessWatcher. Generated by JavaH.
  • ProcessWatcher.c: the calls to the MacOS ProcessManager.
  • ProcessWatcher.h: constant definitions and function prototypes for ProcessWatcher.c.
  • ProcessWatcher.n: project definition file.
  • libstubs_example.c: export information for use by CodeWarrior.

For this last file, I kept the same name as the original included with the JDK, but modified the contents to include NProcessWatcher.stubs.

You can now make this project by pressing Command-M, which will compile and link the code. The compiler should place the shared library file "ProcessWatcher Library" in the project directory.

Java Class Interactions

We can now turn our attention to the Java portion of our program. We have already looked at NProcessWatcher, which contains the native method calls. Figure 3 shows the names of all of the Java class and source code files.

Figure 3. The Java source and class files for the front-end.

Here is the purpose of each Java source code file:

  • Globals: constants used throughout the other classes.
  • NProcessWatcher: calls the native code shared library.
  • PrefsDialog: the preferences dialog.
  • ProcessInfo: data storage for information about each process.
  • ProcessWatcher: the front-end and main window.
  • Updater: the thread which periodically calls NProcessWatcher.

Figure 4 illustrates the interaction between the Java classes and the library.

Figure 4. Class file and library interaction.

These interactions are defined as follows:

  1. The Java front-end(ProcessWatcher) creates a Java thread (Updater), and starts it running.
  2. Updater creates an instance of the class NProcessWatcher, which interfaces with the shared library. NProcessWatcher loads the PPC shared library (ProcessWatcher Library).
  3. NProcessWatcher calls a native function within ProcessWatcher Library, instructing it to retrieve and save the current Process Manager information.
  4. Updater tells ProcessWatcher to request and display the process information.
  5. Updater goes to sleep. On wake up, return to step 3.

In addition, ProcessWatcher calls PrefsDialog when the user clicks the Prefs button. If the user updates the settings, the changes are signaled back to ProcessWatcher.

The Java Front-End

The Java front-end is a window that looks vaguely similar to the "About This Macintosh" window under the Apple Menu (refer to Figure 5).

Figure 5. The finished product.

When launched, the front-end retrieves and displays some information about the operating system and architecture. Then it creates a thread that calls the native code in the shared library. The thread instructs the front-end to update its window, then goes to sleep for a user-specified number of seconds. When it receives the update message from the thread, the front-end communicates with the shared library using the native methods described previously. It retrieves a count of the number of processes, then the partition size, RAM in use, and name for each process. The front-end then draws RAM usage bars in its window.

The front-end has three buttons near the bottom: Quit, Update, and Prefs. Quit leaves the application. Update performs an immediate update of the data displayed in the window. This is the same action taken when the thread informs the window to update, but using the button it can be done on demand. Clicking the Prefs button displays the preferences dialog.

For simplicity, the user interface items are placed using absolute values (screen coordinates). Note that this may affect the look of the front-end if you use it on a non-Macintosh platform. (Of course, you would also have to rewrite the shared library for the new platform.)

The preferences dialog is a moveable modal dialog, as shown in Figure 6. It contains a text field, a scrollbar, and Cancel and OK buttons. The text field shows the sleep setting for the thread (in seconds). It is set to non-editable, and its contents are highlighted. It gets updated with new values as the user moves the scrollbar. The scrollbar is horizontal. Larger values are to the right. Clicking Cancel will dismiss the dialog without saving any changes the user made to the sleep setting. Clicking OK will save the changes, then dismiss the dialog. Settings are kept internally; they are not written out to a preferences file.

Figure 6. The Preferences dialog.

The scrollbar event-handling is related to the text field in this way: when the user clicks in the scrollbar or moves the thumb, the appropriate event is caught, and the number in the text field changes appropriately. Currently, page up and page down events are not handled. Scrollbar event handling is accomplished by overriding the handleEvent() method in PrefsDialog.

The updater thread leads a simple life. Once created, it goes through a continuous cycle. It first creates an instance of the native class, which is responsible for calling the native methods (which invoke the C functions). Next, the thread tells its parent (the front-end) to update its display. Finally, the thread goes to sleep for the user-specified sleep interval. When it wakes up, if the native object has been destroyed for some reason, it gets recreated. Otherwise, the thread instructs both the native object and the parent object to perform their respective update operations.

Listing 6 contains the code for requesting process information from the shared library, and the paint() method used to display it. Here is the method that requests the process information. Notice that it retrieves the process names one character at a time into a character array, then converts that array to a String.

Listing 6: ProcessWatcher.java (partial listing)

ProcessWatcher.java
The front-end for our Java application.

  public void readProcessInfo()
  {
    //  Retrieve the reference to our native class, maintained by the Updater.
    NProcessWatcher  tempNProcessWatcher =
      theUpdaterThread.doGetNativeClass();

    //  If the native class has not been instantiated yet, leave.
    if ( tempNProcessWatcher == null )
      return;

    //  Clear out the Vector.
    theProcessList.removeAllElements();

    //  Counters for traversing the array of processes, 
    //  and the individual process names.
    int  i = 0, j = 0;
    
    //  So we know when we've retrieved all the process elements...
    int  max = tempNProcessWatcher.getNumElements();

    //  Walk through the process elements one at a time.
    for ( i = 0; i < max; i++ )
    {
      //  Retrieve process size info from the native class.
      int theCurRAM = 
            tempNProcessWatcher.getCurrentSize( i );
      int thePartition = 
            tempNProcessWatcher.getProcessSize( i );
      
      //  How long is the process name?
      int theLength = 
            tempNProcessWatcher.getProcessNameLength( i );

      //  We'll hardcode a max name size. It must be at least as 
      //  large as the name size in the C code (32).
      char[] theCharArray = new char[ 32 ];

      //  Get the process name, one character at a time. Note the dual 
      //  counters used to get the char:
      //    i is the current process array element, and
      //    j + 1 is a character in the name (Str32) for that process.
      //  Our character array starts its element numbering at 0, but the Str32 data 
      //  type (used in the C code) uses location 0 for the length of the string, so 
      //  we have to adjust the counter j to start one element beyond it.
      for ( j = 0; j < theLength; j++ )
        theCharArray[ j ] = 
        tempNProcessWatcher.getProcessNameChar( i, j + 1 );

      //  Once the char array is filled in, create a Java String from it.
      String theNameString = new String( theCharArray );

      //  Create a new instance of the ProcessInfo class, fill in
      //  the values, then place the reference to the object into the Vector.
      ProcessInfo tempProcessInfo = new ProcessInfo();
      tempProcessInfo.setCurRAM( theCurRAM );
      tempProcessInfo.setPartition( thePartition );
      tempProcessInfo.setName( theNameString );
      theProcessList.addElement( tempProcessInfo );

      //  Force changes to redraw.
      repaint();
    }

Listings 6 through 8 contain the Java code for the front-end, preferences dialog, and updater thread, respectively. Listing 9 contains definitions for various constants used throughout the Java classes. These listings are fairly well structured, and should be easy to read.

Conclusion

Calling C code from Java requires some preparation. You must properly construct a PowerPC shared library and define methods within your Java classes for calling this library. We explored the steps necessary to get data from the Process Manager to a shared library and then to a Java front-end for display. In situations where you need Macintosh Toolbox functionality that Java does not inherently provide, a shared library can be an effective solution. This approach is supported by Sun, so it makes sense to use it if necessary.

References

  1. Inside CodeWarrior 8, Metrowerks Inc., 1996.
  2. Inside Macintosh: PowerPC System Software, Apple Computer, Inc., Addison-Wesley Publishing Company, 1994.
  3. Teach Yourself Java in 21 Days, Laura Lemay and Charles L. Perkins, Sams.net Publishing, 1996.

URLs

Source files: http://www1.omi.tulane.edu/adowns/MacTech/source/.


Andrew Downs, andrew.downs@tulane.edu, is a programmer for the Office of Medical Informatics at the Tulane University School of Medicine in New Orleans, LA. He also teaches C and Java programming at Tulane University College. Andrew wrote the Macintosh shareware program Net Manager, and the Java application UDPing.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Dashlane 6.2027.0 - Password manager and...
Dashlane is an award-winning service that revolutionizes the online experience by replacing the drudgery of everyday transactional processes with convenient, automated simplicity - in other words,... Read more
ffWorks 2.1.5 - Convert multimedia files...
ffWorks, focused on simplicity, brings a fresh approach to the use of FFmpeg, allowing you to create ultra-high-quality movies without the need to write a single line of code on the command-line.... Read more
Dropbox 101.4.434 - Cloud backup and syn...
Dropbox for Mac is a file hosting service that provides cloud storage, file synchronization, personal cloud, and client software. It is a modern workspace that allows you to get to all of your files... Read more
1Password 7.6 - Powerful password manage...
1Password is a password manager that uniquely brings you both security and convenience. It is the only program that provides anti-phishing protection and goes beyond password management by adding Web... Read more
EyeTV 4.0.0 - Watch and record TV on you...
EyeTV brings a rich TV experience to your Mac. Watch live TV on your Mac. Pause, rewind, and record whenever you want. EyeTV gives you powerful control over what you watch and how you watch it. Put... Read more
Tidy Up 5.3.7 - Find duplicate files and...
Tidy Up is a full-featured duplicate finder and disk-tidiness utility. Features: Supports Lightroom: it is now possible to search and collect duplicates directly in the Lightroom library. Multiple... Read more
Pinegrow 5.97 - Mockup and design web pa...
Pinegrow (was Pinegrow Web Designer) is desktop app that lets you mockup and design webpages faster with multi-page editing, CSS and LESS styling, and smart components for Bootstrap, Foundation,... Read more
BlueStacks 4.210.0 - Run Android applica...
BlueStacks App Player lets you run your Android apps fast and fullscreen on your Mac. Feature comparison chart How to install Bluestacks on your Mac Go to MacUpdate and click the green "Download"... Read more
WhatsApp 2.2027.10 - Desktop client for...
WhatsApp is the desktop client for WhatsApp Messenger, a cross-platform mobile messaging app which allows you to exchange messages without having to pay for SMS. WhatsApp Messenger is available for... Read more
Art Text 4.0.1 - $29.99
Art Text is graphic design software specifically tuned for lettering, typography, text mockups and various artistic text effects. Supplied with a great variety of ready to use styles and materials,... Read more

Latest Forum Discussions

See All

Clash Royale: The Road to Legendary Aren...
Supercell recently celebrated its 10th anniversary and their best title, Clash Royale, is as good as it's ever been. Even for lapsed players, returning to the game is as easy as can be. If you want to join us in picking the game back up, we've put... | Read more »
Steam Link Spotlight - Disco Elysium
Steam Link Spotlight is a feature where we look at PC games that play exceptionally well using the Steam Link app. Our last entry was Signs of the Sojourner Read about how it plays using Steam Link over here. | Read more »
Distract Yourself With These Great Mobil...
There’s a lot going on right now, and I don’t really feel like trying to write some kind of pithy intro for it. All I’ll say is lots of people have been coming together and helping each other in small ways, and I’m choosing to focus on that as I... | Read more »
Pokemon Go's July Community Day wil...
Pokemon Go developers have announced the details concerning the upcoming Gastly Community Day. This particular event was selected by the players of the game after the Gas Pokemon came in second place after a poll that decided which Pokemon would... | Read more »
Clash Royale: The Road to Legendary Aren...
Supercell recently celebrated its 10th anniversary and their best title, Clash Royale, is as good as it's ever been. Even for lapsed players, returning to the game is as easy as can be. If you want to join us in picking the game back up, we've put... | Read more »
Detective Di is a point-and-click murder...
Detective Di is a point-and-click murder mystery set in Tang Dynasty-era China. You'll take on the role of China's best-known investigator, Di Renjie, as he solves a series of grisly murders that will ultimately lead him on a collision course with... | Read more »
Dissidia Final Fantasy Opera Omnia is se...
Dissidia Final Fantasy Opera Omnia, one of Square Enix's many popular mobile RPGs, has announced a plethora of in-game events that are set to take place over the summer. This will include several rewards, Free Multi Draws and more. [Read more] | Read more »
Sphaze is a neat-looking puzzler where y...
Sphaze is a neat-looking puzzler where you'll work to guide robots through increasingly elaborate mazes. It's set in a visually distinct world that's equal parts fantasy and sci-fi, and it's finally launched today for iOS and Android devices. [... | Read more »
Apple Arcade is in trouble
Yesterday, Bloomberg reported that Apple is disappointed in the performance of Apple Arcade and will be shifting their approach to the service by focusing on games that can retain subscribers and canceling other upcoming releases that don't fit... | Read more »
Pixel Petz, an inventive platform for de...
Pixel Petz has built up a sizeable player base thanks to its layered, easy-to-understand creative tools and friendly social experience. It revolves around designing, trading, and playing with a unique collection of pixel art pets, and it's out now... | Read more »

Price Scanner via MacPrices.net

B&H Photo offers $200-$300 discounts on A...
B&H Photo has new 16″ MacBook Pros on sale today for $200-$300 off Apple’s MSRP, starting at $2149. Expedited shipping is free to many addresses in the US: – 2019 16″ 2.6GHz 6-Core MacBook Pro... Read more
Clearance 2019 15″ MacBook Pros on sale today...
Amazon-owned Woot is blowing out Apple refurbished, clearance 2019 15″ MacBook Pros starting at only $1579 and up to $950 off Apple’s original MSRP. According to Woot, “These MacBooks are Refurbished... Read more
Apple Refurbished iMac Pros available for $35...
Amazon-owned Woot is selling Apple refurbished 27″ 3.2GHz 8-Core iMac Pros for $3599.99 shipped. That’s $1400 off Apple’s original MSRP for this model. According to Woot, these iMac Pros are “Factory... Read more
Clearance 2019 13″ 2.4GHz/256GB MacBook Pro o...
B&H Photo has dropped their price on the clearance 2019 13″ 2.4GHz/256GB Quad-Core Silver MacBook Pro by $500 off Apple’s original MSRP to a new low of only $1299. Expedited shipping is free to... Read more
$219 Apple AirPods Pro are back at Verizon, s...
Verizon has Apple AirPods Pro on sale again for a limited time for $219.99 on their online store. Their price is $30 off Apple’s MSRP, and it’s the lowest price we’ve seen for AirPods Pro. Available... Read more
Apple’s $779 13″ MacBook Air deal returns to...
Apple has clearance, Certified Refurbished, 2019 13″ MacBook Airs available again starting at $779. Each MacBook features a new outer case, comes with a standard Apple one-year warranty, and is... Read more
$200 13″ MacBook Pro discounts are back at Am...
Amazon has 2020 13″ 2.0GHz MacBook Pros on sale again today for $150-$200 off Apple’s MSRP. Shipping is free. Be sure to purchase the MacBook Pro from Amazon, rather than a third-party seller, and... Read more
Deal Alert! Apple AirPods with Wireless Charg...
Sams Club has Apple AirPods with Wireless Charging Case on sale on their online store for only $149.98 from July 6, 2020 to July 9, 2020. Their price is $50 off Apple’s MSRP, and it’s the lowest... Read more
Xfinity Mobile promo: Apple iPhone XS models...
Take $300 off the purchase of any Apple iPhone XS model at Xfinity Mobile while supplies last. Service plan required: – 64GB iPhone XS: $599.99 save $300 – 256GB iPhone XS: $749.99 save $300 – 512GB... Read more
New July 2020 promo at US Cellular: Switch an...
US Cellular has introduced a new July 2020 deal offering free 64GB Apple iPhone 11 smartphones to customers opening a new line of service. No trade-in required, and discounts are applied via monthly... Read more

Jobs Board

Product Manager, *Apple* Commercial Sales -...
Product Manager, Apple Commercial Sales Austin, TX, US Requisition Number:77652 As an Apple Product Manager for the Commercial Sales team at Insight, you Read more
Physical Therapist Assistant - *Apple* Hill...
Physical Therapist Assistant - Apple Hill Rehab - Full Time Tracking Code 62519 Job Description General Summary: Under the direct supervision of a licensed Physical Read more
Operating Room Assistant, *Apple* Hill Surg...
Operating Room Assistant, Apple Hill Surgical Center - Full Time, Day Shift, Monday - Saturday availability required Tracking Code 62363 Job Description Operating Read more
Perioperative RN - ( *Apple* Hill Surgical C...
Perioperative RN - ( Apple Hill Surgical Center) Tracking Code 60593 Job Description Monday - Friday - Full Time Days Possible Saturdays General Summary: Under the Read more
Product Manager, *Apple* Commercial Sales -...
Product Manager, Apple Commercial Sales Austin, TX, US Requisition Number:77652 As an Apple Product Manager for the Commercial Sales team at Insight, you Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.