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.
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 functions can be classified in three categories:
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 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:
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.
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.
With Java Platform Invoke SDK you can define Class method as a callback function:
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);
}
}
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 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();
}
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.
With Java Platform Invoke SDK you can bind C/C++ class virtual table functions with Java class methods: