How to use Java Platform Invoke (J/Invoke) API

 

Introduction

This overview describes API that makes possible simple calls of export functions from native libraries. In some Java application Developer needs to call functions from System libraries. In this case Java programmer should write JNI code. But the most Java programmers cannot write JNI libraries or have a number of difficulties while developing JNI code.

In Microsoft's .NET languages there is API called Platform Invoke. Its main idea is "declare" and "use". In common case Programmer does not use specific marshaling for function parameters.

System requirements

Before installing be sure your computer meets the system requirements:

Ø      Operating Systems

·        Windows NT 4.0

·        Windows 2000

·        Windows XP

·        Windows Server 2003

·        Linux

Ø      Java Runtime Environment

·        SUN JSE1.5.x and later

·        IBM JDK1.5.x and later

·        GTK-2.x and later

Native Function Types

Native functions can be classified in three categories:

 

Java and Native Type Maps Supported by J/Invoke

The following table lists data types used in the Win32 API and C-style functions. Many native libraries contain functions that pass these data types as parameters and return values. In some cases, you can substitute a type of the same size for the type listed in the table.

 

Java Type

Native type in wtypes.h

Native C language type

Description

com.compose.jni.IntPtr

LPVOID

void*

32 bits

int

HANDLE

void*

32 bits

byte

BYTE

unsigned char

8 bits

byte

CHAR

char

8 bits

short

SHORT

short

16 bits

short

WORD

unsigned short

16 bits

char

WCHAR

wchar_t

16/32 bits

long

LONG

long

32 bits

long

INT, UINT

int

32 bits

int

INT, UINT

int

32 bits

double

DOUBLE

double

64 bits

float

FLOAT

float

32 bits

String

LPWSTR, LPCWSTR

wchar_t*

Decorate with Unicode

String

BSTR

wchar_t*

Decorate with Unicode

String

LPSTR, LPCTSTR

char*

Decorate with ANSI

char[]

LPWSTR, LPCWTSTR

wchar_t*

 

byte[]

LPSTR, LPCTSTR

char*

 

int[]

 

int*

 

long[]

 

long*

 

long[]

 

int*

 

short[]

 

short*

 

float[]

 

float*

 

double[]

 

double*

 

String[]

BSTR*

BSTR*

 

String[]

 

char**

 

int[]

 

void*

 

Class extends com.compose.jni.CStructure

LPVOID

void*

32 bits

Array

LPVOID

void*

32 bits

Java Method wrapped with com.compose.jni.CCallback class

LPVOID

void*

32 bits

 

All array members of CStructure class instance marshaled "flat" to C structure. In the next version of the product will be option to Marshal Structure members as flatten (by value) or pointer (by reference).

Java Platform Invoke Internals

Java Platform Invoke relies on Java byte code data to locate exported functions and marshal their arguments at run time.

 

When Java platform invoke calls a native function, it performs the following sequence of actions:

  1. Locates the DLL containing function.
  2. Loads the DLL into memory.
  3. Locates the address of the function in memory and pushes its arguments onto the stack, marshaling data as required.
  4. Transfers control to the native function.

 

Defining native function prototypes

Java platform invoke an API that enables Java code to call native functions implemented in dynamic link libraries (DLLs), such as those in the Win32 API. It locates and invokes an exported function and marshals its arguments (integers, strings, arrays, structures, and so on) across the interoperation boundary as needed. This API supports implicit marshaling that makes Java coding with native functions very simple.

To consume exported DLL static functions you need

  1. Identify functions in DLLs.
    Minimally, you must specify the name of the function and name of the DLL that contains it.
  2. Create a class to hold DLL functions.
    Create an individual class for each native function, or create one class that contains a set of related native functions. This class should extend CNativeLibrary and call in a static block the method bindMethodsOf with this class object as a parameter. Use the annotation ImportLibrary to define the native library name (it should be defined without extension, i.e. .dll, .so).
  3. Create prototypes in Java code.
    Write prototype of a native function with signature that corresponds to the native function using data type map agreement above mentioned. To identify the native function use the annotation Function. Mark the method with the native and static modifiers.
  4. Call a DLL function.
    Call the method on your Java class as you would any other Java native static method. Passing structures and implementing callback functions are special cases.

 

Defining native function

 

The identity of a DLL function consists of the following elements:

 

For example, specifying the MessageBox function in the User32.dll of MS Windows OS identifies the function (MessageBox) and its location (User32.dll, User32, or user32). The Microsoft Windows application programming interface (Win32 API) can contain two versions of each function that handles characters and strings: a 1-byte character ANSI version and a 2-byte character Unicode version. In Linux/Unix there is also a 4-byte Unicode version. When unspecified, the character set, represented by the CharSet value in Annotation @Funciton, defaults to ANSI.

Some functions can have more than two versions.

 

MessageBoxA is the ANSI entry point for the MessageBox function; MessageBoxW is the Unicode version. You can list function names for a specific DLL, such as user32.dll, by running a variety of command-line tools.

 

You can rename an unmanaged function to whatever you like within your code as long as you map the new name to the original entry point in the DLL. An entry point in your Java code identifies the location of a function in a DLL. Within a Java project, the original name entry point of a target function identifies that function across the interoperation boundary. Further, you can map the entry point to a different name, effectively renaming the function.

Following is a list of possible reasons to rename a DLL function:

 

You must use the entryPoint field in Annotation @Function to specify a dynamic/shared library function by name. Use the following an annotation form to indicate a name:

 

@Function(entryPoint = "MessageBoxA", charSet = CharSet.Ansi, convention = CallingConvention.Stdcall)

 

The field convention that identifies the calling convetion of the function is optional, if not defined the system default value is CallingConvention.Stdcall.

The following example demonstrates how to define MessageBoxA EntryPoint in your Java code.

 

@ImportLibrary(libName = "user32")

public class User32 extends CNativeLibrary {

      static{

            bindMethodsOf(User32.class);

      }

@Function(entryPoint = "MessageBoxA", charSet = CharSet.Ansi, convention = CallingConvention.Stdcall)

      public static native int MessageBox(long hwnd, String text, String title, long btn);

}

 

The example above shows that native function prototypes are inserted into a class that extends com.compose.jni.CNativeLibrary Class and marked with @ImportLibrary annotation that has the only field libName. It should be initialized with the library name, which prototypes are defined in the class. This class should also have the mandatory static block with a static function call bindMethodsOf for binding Java prototypes with an original native module.

 

Platform invoke enables you to control a significant portion of the operating system by calling functions in the system libraries. It also makes possible call native functions from other native modules without JNI coding.

Examples that demonstrate how to construct Java-based declarations to be used with platform invoke are in demo namespace.

Callback function

With Java Platform Invoke SDK you can define Class method as a callback function:

  1. Write Java Class with one non-static method (which is a prototype of a native callback function). This class should extend com.compose.jni.CCallback.
  2. Annotate the callback method with the annotation Function.
  3. If you need mark the method arguments with annotations: @In, @Out, @MarshalAs.

For example, let define a callback method that accepts integer n and MyStructure object as a structure. This method returns to a native caller the structure object with modified s field. The field s is mapped to a native array as a set of Unicode 4-byte (in MS Windows 2-byte) characters, ms is only output argument (that is, it does not pass values from native code).

 

@StructLayout(layout = LayoutKind.Sequential)

class MyStruct extends CStructure {

    int[] i = new int[3];

 

    @FieldLayout(charSet = CharSet.Unicode4)

    char[] s = new char[100];

}

 

public class CallbackFn extends CCallback {

     

      @Function

      public void callbackFn(int n, @Out MyStruct ms){

            if(ms != null){

            System.arraycopy(new int[]{2999,3999,4999}, 0, ms.i, 0, 3);

            char[] ca = new String("some data returned").toCharArray();

            System.arraycopy(ca, 0, ms.s, 0, ca.length);

      }

}

Marshaling

Java Platform Invoke SDK supports implicit and explicit marshaling. In 90% the engine can do right parameter marshaling in a native function call. The performance of convertion the data from java object to a native function parameters code and moving the result returned back to Java code depends on a number of memory allocations and deletions. Some data types can be allocated with different allocators. For example, in one hand, text data memory in MS Windows can be allocated with malloc, CoTaskMemAlloc, SysStringAlloc, etc. and marshaling algorithm should “know” how the data pointer to be created. In this case marshaling type should be defined explicitly. In other hand, some pointers returned by a native function may not be deleted, etc.

Another problem with implicit marshaling is that by default all function parameters are marshaled and unmarshaled that reduces application performance. Using custom marshaling you can define parameters only marshaled and only unmarshaled.  For example, MessageBox function can be defined as follows

 

@ImportLibrary(libName = "user32")

public class UserLib extends CNativeLibrary {

  @Function(entryPoint = "MessageBoxA", charSet = CharSet.Ansi)

  public static native int MessageBox(int hwnd, @In String msg, @In String caption, int msgtype);

}

 

C/C++ structures as Java classes

C/C++ structures are mapped to Java class by defining marshaling explicitly either for all Java class or for each Java class field. For example, well-known Windows structures MSG and POINT structures can be defined with Java Platform Invoke SDK in a very simple way

 

@StructLayout(layout = LayoutKind.Sequential)

public class POINT extends CStructure {

  public int x;

  public int y;

}

 

@StructLayout(layout = LayoutKind.Sequential)

public class MSG extends CStructure {

  public int hwnd;

  public int message;

  public int wParam;

  public int lParam;

  public int time;

  public POINT  pt = new POINT();

}

 

@StructLayout defines how the Java class needs to be arranged while marshaling its data. There are two structure layouts: Sequentional and Explicit. Sequentional is used to force the members to be laid out sequentially in the order they appear, see the sample above. Explicit controls the precise position of each data member. With Explicit, each member must use @FieldLayout Annotation to define an ordinal number for indicating the position of the field in the memory. For example, two nested structures can be define in Java as follows:

 

@StructLayout(layout = LayoutKind.Sequential)

public class SIGNSCALE extends CStructure {

      public byte scale = 0;

 

      public byte sign = 0;

};

 

@StructLayout(layout = LayoutKind.Explicit)

public class DECIMAL extends CStructure {

 

      @FieldLayout(ordinal = 0)

      public short wReserved = 0;

 

            @FieldLayout(ordinal = 2)

            public long[] Lo64 = new long[]{0, 0};

 

            @FieldLayout(ordinal = 1)

            public SIGNSCALE sign_scale = new SIGNSCALE();

}

 

Unions

 

This sample demonstrates how to unions with Java classes. Let there is a union of structures in C++ code:

 

struct MyStruct1

{

    int number;

    double d;

};

 

struct MyStruct2

{

    int i;

    char str[128];

};

 

union MyUnion

{

    MyStruct1 sct1;

    MyStruct2 sct2;

};

 

In Java code unions declared as classes with Explicit layout:

 

@StructLayout(layout = LayoutKind.Sequential)

public class MyStruct

{

    public int number = 0;

    public double d = 0.0;

}

 

@StructLayout(layout = LayoutKind.Sequential)

public class MyStruct2

{

    public int I = 0;

    public char[] str = new char[128];

};

 

@StructLayout(layout = LayoutKind.Explicit)

public class MyUnion

{

    @FieldLayout(ordinal = 0)

    public MyStruct1 sct1 = new MyStruct1();

 

 

    @FieldLayout(ordinal = 0)

    public MyStruct2 sct2 = new MyStruct2();

}

 

The other structure examples see in demo namespace.

Using C++ class object with virtual member functions in Java code

With Java Platform Invoke SDK you can bind C/C++ class virtual table functions with Java class methods:

  1. Write and implement a native static function that creates the C/C++ class instance (named as peer) if it is not available. (In Windows OLE SDK this function is CoCreateInstance).
  2. Define Java class(es) with prototypes of the native functions using the same rules as for static native functions. But instead of Function Annotation use InterfaceFunction.
  3. In InterfaceFunction define the ordinal number of each native function in C/C++ class virtual table.
  4. If you wrote more than one class with native function prototypes from one C/C++ class virtual table, which subclass each other (see IDispatch extends IUnknown in demo namespace) than base class (IUnknown in demo) should extend com.compose.ole.system.CNativeInterface.
  5. Call in some constructor of your class (in the example see a constructor) which wraps a native class virtual table, calls the method bindMethodsOf with peer and the current Thread ID.
  6. Create your Java class object by calling this constructor.