JNIWrapper Programmer's Guide


Version: 3.12
Last Updated: October 21, 2016

Table of Contents
1. Introduction
1.1. About This Guide
1.2. New in This Version
1.3. Related Documents
1.4. About JNIWrapper
1.4.1. Technical Advantages
1.4.2. New in This Release
2. Getting Started
2.1. System Requirements
2.2. Package Contents
3. Configuring JNIWrapper
3.1. Library JAR File
3.2. Native Code Library
3.3. License File
4. Working with Native Libraries and Functions
4.1. Finding Native Libraries
4.2. Using DefaultLibraryLoader
4.3. Loading Native Libraries
4.4. Getting Functions from a Native Library
4.4.1. Managing Library's Global Variables
4.5. Calling Conventions
4.5.1. Calling Conventions on Windows
4.5.2. Calling Conventions on Linux/Mac OS X
5. Passing Parameters to/from Native Code
5.1. Primitive Types
5.1.1. Mappings of Native Types to JNIWrapper Classes
5.2. Structures and Unions
5.2.1. Setting Structure Alignment
5.2.2. Setting an Active Union Member
5.3. Pointers
5.3.1. Function Pointers
5.3.2. ArithmeticalPointer Class
5.3.3. Casting Pointers
5.4. Arrays
5.4.1. Pointers to Array Contents
5.4.2. Controlling Memory Allocation for Arrays
5.5. Strings
5.5.1. Str Class
5.5.2. StringArray Class
5.6. Enumerations
6. Calling Native Functions
6.1. Calling Conventions
6.1.1. A Quick Way to Call a Function
6.2. Using Callbacks
7. Working with Multithreading
7.1. Parameters
7.2. Functions
8. Code Generator for JNIWrapper
8.1. Overview
8.2. Running Code Generator for JNIWrapper
8.3. Specifying Input Parameters and Options
8.4. Using Type Replacement Table
8.5. Namespaces and Package Naming
8.6. Supported Types
8.7. Known Limitations
8.8. Example
9. Using AWT Native Interface
9.1. Using JAWT Support
9.2. Accessing Native Control's Data
9.3. Getting HWND of a Window
9.4. JAWT Support in Different JDK Versions
10. Using JNIWrapper in Java Web Start Applications
11. Using JNIWrapper in Applets
12. Using JNIWrapper in Native Executable Archives
13. Using JNIWrapper in Servlets
14. Support
14.1. Reporting Problems
14.2. Troubleshooting
15. Where to Get a New Version

Chapter 1. Introduction


In the few years since its first release, the JavaTM programming language has grown immensely to become a popular platform. Many developers working on different platforms find their own advantages in using Java technology. One of them is, of course, the "write once, run anywhere" ability allowing developers to write software on one platform and run it on another.

Sometimes, however, Java programs have to interact with native code. This is well justified for such reasons as performance, a lack of features for platform integration in the Java platform or the need of legacy software interoperability. To solve this problem, Java Native Interface (JNI) was introduced in the Java platform, allowing programmers to write native code pieces and integrate them into their Java programs. The main difficulty arising from such an approach is that native code is completely disjoint from Java code in terms of writing, browsing, debugging and maintenance.

In this document, we are introducing JNIWrapperTM - the product that allows developers to interface native code while retaining full control of the application on the Java side at any level.

1.1. About This Guide

This guide introduces JNIWrapperTM, describes its design goals, concepts and principles, provides the requirements for using the product as well as sufficient information you need to know before starting to work with the product.

This document covers all platform versions of JNIWrapper. In cases where functions treat a particular platform in a specific way, or specific configuration settings are needed, these are marked accordingly.

1.2. New in This Version

New in this version (3.0) of the Programmer's Guide:

Added: Chapter 8.

1.3. Related Documents

The documents provided on the Documents page at the JNIWrapper area at TeamDev site (http://www.teamdev.com) are intended to help you understand and effectively use of the JNIWrapper technology.

To make your learning curve shorter and smoother, we suggest that you read the documentation in the following order:

  • You can start with the current Programmer's Guide document describing the ideas and basics of the proposed software.

  • If you plan to develop your own software effectively, proceed from this Programmer's Guide to JNIWrapper Tutorial, which provides step-by-step instructions with code samples.

  • Each version of JNIWrapper is supplied with updated Release Notes. To receive the up-to-date version of specific information, be sure to check the Release Notes section inside Readme.txt file before installing JNIWrapper.

  • You can also find helpful information in the Frequently Asked Questions document, which we regularly update based on the questions we get from our users. The document can be accessed online at the JNIWrapper site.

1.4. About JNIWrapper

JavaTM is a very powerful platform allowing programmers to develop state-of-the-art software. It is, however, designed to run on a variety of different platforms and therefore does not include every feature of every platform. Certain basic things like creating a symbolic link under Linux or operating with registry items under Windows® are not supported in JavaTM. Programmers willing to do this are forced to write a native library and classes interfacing with it, then debug the code using two different debuggers (Java- and native-side). These are sometimes difficult and always very time-consuming tasks. All of them can be avoided by using JNIWrapper - a Java library for calling native library functions. With JNIWrapper, you can extensively use the potential of the underlying platform (like tray icons or custom shape splash screens) with only a single native library, having the full control over the program flow on the Java side.

1.4.1. Technical Advantages

JNIWrapper has a number of technical advantages over the competitors. The most important of them are:

  • Comprehensive native function invocation support

    JNIWrapper supports both stdcall and cdecl calling conventions and all complex C types including structures and unions. Callbacks are fully supported with any parameter and return types and both calling conventions. For unexpected cases, you can create your own types taking full control over parameter behavior.

  • Automatic resource management

    • All resources allocated by JNIWrapper components are released automatically when no longer required. You can treat JNIWrapper variables as ordinary objects that can be picked up by Java garbage collector.

    • JNIWrapper objects are also safe with regard to finalizers: all resources are guaranteed to be available during finalization.

  • High performance

    This has always been our priority. JNIWrapper has been specially tuned for performance, particularly in cases where large amounts of data are involved in the interactions.

  • Minimum behind-the-scenes operation

    You should always be able to see and understand what is happening when they work with native-side data. This helps both to develop and debug complex interactions between Java and native code.

The product is extensively used in the projects carried out by our company, which ensures its efficiency, reliability, future support, and improvement.

1.4.2. New in This Release

For detailed information about the changes in the current version, check the Readme.txt inside the JNIWrapper package.

The JNIWrapper changes history is also available online at:

http://www.teamdev.com/jniwrapper/whats_new.jsf

Chapter 2. Getting Started


2.1. System Requirements

The following are general requirements to run JNIWrapper on the following supported platforms:

  • Windows

    • OS: Windows 9x, Me, NT 4.0, 2000, XP and Vista

    • Java: Java 2 SDK/JRE 1.3.x and later. For CodeGen GUI, the preferred JDK is 1.4.2. or later.

  • Linux

    • OS: Red Hat Linux 7.2, 9.0; Mandrake Linux 9.1, 9.2, 10.0; Mandriva 2005 LE; WhiteBox Linux; Gentoo Linux (in general, all distros on kernel 2.4.x and kernel 2.6.x)

    • Java: Java 2 SDK/JRE 1.4.x

  • Mac OS X

    • OS: Mac OS X 10.3 (Panther) and later

    • Java: Java 2 SDK/JRE 1.4.x

There are no specific memory or other hardware requirements for developing an application based on JNIWrapper.

2.2. Package Contents

The JNIWrapper package consists of the following main files required for work:

  • Library JAR file - jniwrap-<version>.jar, where <version> is the product version, for example "3.0".

  • Native code library

    • for Windows - jniwrap.dll

    • for Linux - libjniwrap.so

    • for Mac OS X - libjniwrap.jnilib

  • License file

    • jniwrap.lic

All the files need to be placed in the appropriate locations. Please see the section "Configuring Software" for more details about the product installation instructions. The package may also contain other files providing some useful information for the you, for example the Readme.txt file.

Chapter 3. Configuring JNIWrapper


JNIWrapper consists of three main files required for the software functioning: a JAR file, native code library, and a license file. The following sections describe where each file should be located. No other configuration is required.

3.1. Library JAR File

The JNIWrapper JAR file should be located in the program class path. Due to the limitations of the Java native library loading mechanism, it is not recommended to load JNIWrapper in custom class loaders, unless you are sure that it will be loaded in only one such class loader.

The library file can also be placed in the boot class path or in the extension directory of Java runtime, but this is not required.

3.2. Native Code Library

The JNIWrapper native code library is loaded using the standard Java native code loading mechanism. There are no known problems with placing the native code library file on a mapped drive or even using it from the network share using a UNC path.

Important

Do not rename the library file, or else it will not be loaded.

Even though the native code library can be placed virtually anywhere, its actual location should be determined considering the fact that Java code must find the library to load. It can be placed somewhere within the program library search path (value of the java.library.path system property, which is by default equal to the value of the system variable PATH on Windows or LD_LIBRARY_PATH on Linux).

Alternatively, you can add a search path to the default library loader used by JNIWrapper or even write a custom one that searches for native code in a predefined location. Using a default path may be preferable for development and library loader as a much better way for distributing a complete application.

Since version 3.0 it is possible to keep native libraries within a JAR file. JNIWrapper will automatically locate and install a library on demand.

You may want to install the native code library into the directories on the default system path, for example:

  • on Windows - the root of Windows installation or Windows\System32

  • on Linux - <java_home>/lib/i386 or <java_home>/jre/lib/i386

  • on Mac OS X - /usr/lib

Note that this requires having appropriate access rights on the Windows NT/2000/XP, Linux and Mac OS X systems. Installing the native code library by using this way may be convenient, but is not a required procedure.

3.3. License File

Placing the license file is very simple: it should be located in the same folder where the native JNIWrapper library (jniwrap.dll file) resides.

Important

Do not rename the license file, or else it will not be recognized.

Also, there is one universal way for redistribution of the runtime license file. You will just need to save this file to your application's JAR file, into its META-INF subfolder. For example, if you have some application called <application_name>.jar, the jniwrap.lic file should be located in the following folder:

    <application_name>.jar
        \META-INF
            jniwrap.lic

You may appear to have several licenses for JNIWrapper for different platforms supported. In this case, you need to put all the license key files as described above but make sure there is no file name conflict. JNIWrapper accepts multiple license files named like shown below:

  • jniwrap.lic

  • jniwrap.lic1

  • jniwrap.lic2

  • ...

  • jniwrap.lic999

Chapter 4. Working with Native Libraries and Functions


4.1. Finding Native Libraries

JNIWrapper accesses native libraries by name using library loaders. Library loaders are responsible for finding and loading a library by its name. You can use the supplied implementation of a path-based library loader or create your own.

The latter approach requires implementation of the com.jniwrapper.LibraryLoader interface. This interface defines two methods: findLibrary() for finding a file containing the required library by name, and loadLibrary() for loading the library (by calling System.load), also by name.

Suppose you have implemented a library loader called MyLibraryLoader. To make JNIWrapper look for the libraries using this loader, you can write something like this:

    Library.setDefaultLibraryLoader(new MyLibraryLoader());

Remember that the default loader specified this way is also used for loading the JNIWrapper native library.

Alternatively, you may use a custom loader to load a specific library only:

    myLibrary.load(new MyLibraryLoader());

4.2. Using DefaultLibraryLoader

JNIWrapper comes with a convenient default implementation of the LibraryLoader interface: DefaultLibraryLoader. It looks for the libraries in a set of directories (path). The initial search path includes all directories from the java.library.path system property.

New directories can be added to the search path using the addPath() method. DefaultLibraryLoader is a singleton and is also the one JNIWrapper uses by default.

If using the default path with some additions is sufficient, then adding directories to the search path is the only configuration needed. For example, if native libraries (or the JNIWrapper native code) are to be looked for in the bin directory relative to the program working directory, the following line should be added to the JNIWrapper initialization part of your program:

    DefaultLibraryLoader.getInstance().addPath("bin");

4.3. Loading Native Libraries

Loading of libraries does not require special attention, if only one library loader is used, i.e. when the necessary library is found and loaded using the loader set by the setDefaultLibraryLoader() method (or an instance of the DefaultLibraryLoader class, which is the initial value of that property).

If a custom library loader should be used, an explicit call to the library's load() method should be made before any function is loaded from that library.

Take a look at these examples.

Using one library loader:

    Library kernel32 = new Library("kernel32");
    // Use kernel32 here

Using a custom library loader:

    Library customLib = new Library("myCustomLib");
    LibraryLoader myCustomLoader = new MyCustomLibraryLoader();
    customLib.load(myCustomLoader);
    // Use customLib here

4.4. Getting Functions from a Native Library

Functions are represented by instances of the com.jniwrapper.Function class. A function is always part of some library and therefore, cannot be instantiated on its own. To get a function from the library, the getFunction() method should be used. For example:

    Function getLastError = kernel32.getFunction("GetLastError");

Please note that some compilers may mangle function names to include argument types or sizes. This version of JNIWrapper performs search only by the exact name, not by mangled names. Therefore, if you need to invoke a function with such a name, you should specify the full mangled name. For example:

    Function myFunc = myLibrary.getFunction("_myFunc@4");

Most libraries, however, export nice unmangled names. You can find out an exported function name by using tools such as dumpbin or Dependency Checker.

4.4.1. Managing Library's Global Variables

Some global variable called _data is declared in the native library. To find a global variable on the Win32 platform, we can use the following example:

extern "C" __declspec (dllexport) int* _data = new int(123);

To retrieve a value of the variable in the library, we can use the Library.getVariable() method. The sample code below shows how it can be done:

 // load library
 Library SAMPLE_LIB = new Library("Library_Name");

 Int result = new Int();

 // get a variable pointer
 SAMPLE_LIB.getVariable("_data", new Pointer(result));

 // read a variable value
 long variableValue = result.getValue();

4.5. Calling Conventions

There are two widespread calling conventions: cdecl used in most C/C++ programs and stdcall used, for example, in Pascal. Calling convention is a function property since a library can export functions that use different calling conventions. Use your library documentation to find out which calling convention is used by the functions you are going to invoke.

4.5.1. Calling Conventions on Windows

Most Windows® API functions use the stdcall calling convention. This is the default convention used by JNIWrapper. If the function you need to call uses a different calling convention, you should set it in the function object. For example:

    cdeclFunction.setCallingConvention(Function.CDECL_CALLING_CONVENTION);

4.5.2. Calling Conventions on Linux/Mac OS X

The majority of libraries on Linux use the cdecl calling convention. This is the default calling convention on Linux.

Chapter 5. Passing Parameters to/from Native Code


In JNIWrapper, parameters are passed to and from native code using com.jniwrapper.Parameter objects. These objects behave like variables of different types depending on the actual subclass used.

All parameters are mutable, which means that you can change any value at any time. All parameters can be shared across function calls, i.e. one can pass the result of one function call directly to another without creating a new object.

5.1. Primitive Types

JNIWrapper provides parameter classes for all the primitive types available in the native program: signed and unsigned integers of different sizes, floating point values, single-byte and wide characters, etc. Related types usually have a common superclass or implement a common interface.

All simple types have a no-argument constructor that initializes a parameter to the default value (usually zero or equivalent), a constructor with the initial value and a copying constructor. They also have appropriately typed getValue() and setValue() methods. Take a look at the example of using the DoubleFloat parameter:

    DoubleFloat d1 = new DoubleFloat(1.2345);
    DoubleFloat d2 = new DoubleFloat(d1);
    d1.setValue(9.876);
    System.out.println(d2.getValue());

5.1.1. Mappings of Native Types to JNIWrapper Classes

Below is given a table of the mappings for most commonly used data types along with some comments. For more information, visit http://www.teamdev.com/jniwrapper/nativeTypes.jsf

Table 5-1. Mappings of Native Types to JNIWrapper Classes

Native Type (C/C++)JNIWrapper typeComments
Boolean Types
boolBool(1 byte), IntBool(4 bytes) 
Character Types
charChar 
wchar_tWideChar 
uchar *Char, UInt8 
Integer Types
shortShortIntThe unsigned types are represented by prefixing U to the type name. For example, the unsigned int (or unsigned) type is UInt.
intIntThere are also types for predefined-width integers: Int8, Int16, Int32 and Int64, they also have the unsigned variants.
longLongInt 
Floating-point Types
floatSingleFloat 
doubleDoubleFloat 
long doubleLongDoubleLong double is the same as double (8-byte floating-point value) on the Win32 platform.
Pointer Types (not arrays)
void *Pointer.Void 
constPointer.ConstUse Pointer.Const if the referenced object is not to be modified by the calling function.
type *PointerTo create a pointer to the value (variable) of a known type, use the Pointer class. For example: int *i; is Pointer i = new Pointer(new Int());
type *Pointer.OutOnlyUse Pointer.OutOnly if the referenced object is not to be read by the calling function.
char *AnsiString 
wchar_t *WideString 
Arrays
<c primitive type>[n]PrimitiveArray(<corresponding JNIWrapper type>.class, n); 

5.1.1.1. Mappings for Windows API Types

Windows API includes many data types which are not listed here (for example, DWORD, HANDLE). If you need to use one of such types, use Windows-specific documentation such as MSDN to find out the actual C type that corresponds to it (for example, LPSTR corresponds to char*), and use the relevant JNIWrapper type for the argument. You can also check the online Windows Data Types table available at http://www.teamdev.com/winpack/windowsTypes.jsf

5.2. Structures and Unions

JNIWrapper supports C-like structures and unions. Like in C, the structure and union definitions are similar. Both provide a constructor that takes an array of parameters defining the structure or union contents.

If you want to create a reusable Java class for a structure or unions, you can use a protected no-argument constructor and initialize the content by calling the init method. This method is provided for convenience, because class fields are initialized after the superclass constructor is invoked and therefore, cannot be used as constructor arguments. The structure and union member values are accessed by directly querying the parameters specified in the constructor for their values. For example:

/*
Structure definition in C:
struct SomeStruct
{
        int a;
        char b;
        double c;
}
*/
Int intField = new Int();
Char charField = new Char();
DoubleFloat doubleField = new DoubleFloat();

struct = new Structure(new Parameter[] {intField, charField, doubleField});
doubleField.setValue(10); // set struct.a value to 10
// invoke some code that modifies the structure here
System.out.println(doubleField); // prints new value of struct.c

To learn how to use linked structures, open:

<jniwrapper-3.x-win32.zip>/samples/src/LinkedStructureSample.java

5.2.1. Setting Structure Alignment

Structures can be defined with different alignments. The structure alignment is specified in the constructor (or in the call to the init method). Here is the example of a reusable structure definition that supports different alignments:

private static class TestStruct extends Structure {
    public Int _int = new Int();
    public Int8 _int8 = new Int8();
    public Int16 _int16 = new Int16();
    public Int32 _int32 = new Int32();
    public Int64 _int64 = new Int64();
    public LongInt _long = new LongInt();

    public TestStruct() {
        init(new Parameter[] {
            _int, _int8, _int16, _int32, _int64, _long
        });
    }

    public TestStruct(short alignment) {
        init(new Parameter[] {
                _int, _int8, _int16, _int32, _int64, _long
            }, alignment);
    }
}

5.2.2. Setting an Active Union Member

Unlike C, in JNIWrapper you need to define the active union member explicitly using the setActiveMember() method. However, this can be done after the function call where the union data was modified. Take a look at the following examples of the union usage:

union = new Union(new Parameter[] {intField, charField, stringField, structField});
union.setActiveMember(stringField);
stringField.setValue(STRING_VALUE);
func1.invoke(result, union);
// ...
func2.invoke(union, (Parameter[])null);
union.setActiveMember(structField, true);
assertEquals(X_VALUE, structField.getX());

Structures and unions can consist of any simple or complex members that are JNIWrapper parameters. In the above example, one of the union members is a structure.

5.3. Pointers

JNIWrapper supports C-like pointers to all the defined types. You can create a pointer by instantiating the com.jniwrapper.Pointer class. Pointers should always refer to some object. For example:

Pointer pInt = new Pointer(new Int());

A pointer automatically allocates memory for its referenced object; it handles reads and writes requiring the referenced object to read or write its data when the pointer itself is read or written. Thus, after any native function call, all the parameters are updated even if they are referenced by several nested pointers. For example:

Int value = new Int();
Pointer ppInt = new Pointer(new Pointer(value));

// invoke func(int **i) passing ppInt as a parameter
func.invoke(null, ppInt);
System.out.println(value) // value is updated!

In cases where the referenced object is read-only or write-only, one can use the Pointer.Const or Pointer. OutOnly types, respectively. Note, however, that using these classes cannot enforce the read-only or write-only policy on the native function which may still access the data inappropriately. It is recommended that these classes are only used for performance improvements.

JNIWrapper supports pointers that refer to undefined values (void* in C) through the Pointer.Void class. Use this class when you do not care about the actual referenced object and do not need to allocate the memory a pointer points to. For example, if you need a constant (HWND)-1 you can use the following construct:

Pointer.Void HWND_TOPMOST = new Pointer.Void(-1);

Pointer.Void is not a pointer. It doesn't have a referenced object and it is not assignable to and from any other kind of pointer. The name of the type only reflects the fact that this parameter always has the same size as a platform-dependent pointer.

5.3.1. Function Pointers

Another capability of the Pointer.Void class is that it can also be used to represent a function pointer and to call the function it points to. The asFunction() method returns a function object that can be used to invoke a function that a given pointer points to.

Consider the following example. A native library provides a function to install a callback that returns an old callback function pointer:

typedef void (*PCallbackType)(int);
PCallbackType installCallback(PCallbackType);

One can install a hook to monitor the callback invocation in the following way:

// Field declaration
Pointer.Void myOldCallback;
// ...
// Callback installation code
installCallback.invoke(myOldCallback, new HookCallback());

and later inside the HookCallback class:

protected void callback() {
    // do some hook stuff
    myOldCallback.asFunction().invoke(null, intParam);
}

To create an object callable from native code, use the Callback class.

5.3.2. ArithmeticalPointer Class

Limited pointer arithmetics is supported through the ArithmeticalPointer class. This pointer also manages one referenced object, but also accommodates an offset from its initial value. Such a pointer can be used for passing to the functions such as strtok that offset a pointer to iterate through data. Note that the referenced object cannot be changed and is always read and written at its initial offset.

5.3.3. Casting Pointers

A typed pointer represented by an instance of the Pointer class can be cast to an un-typed (i.e. void) pointer represented by an instance of the Pointer.Void class and vice versa. The following code demonstrates casting the instance of the Pointer.Void class to the instance of the Pointer class:

  // retrieve the pointer somewhere
  Pointer.Void handle = getHandle();

  // prepare the data pointer, assuming that it points to an integer value
  Int data = new Int();
  Pointer dataPtr = new Pointer(data);

  // cast the pointers
  handle.asTypedPointer(dataPtr); 

After the last operation, the data variable obtains the value which the handle pointer points to.

Casting an instance of the Pointer class to an instance of the Pointer.Void class is done as shown on the sample below:

  // create typed pointer
  Int data = new Int(123);
  Pointer dataPtr = new Pointer(data);

  // create void pointer
  Pointer.Void handle = new Pointer.Void();

  // cast the pointers
  dataPtr.asVoidPointer(handle);

After the last operation, the handle pointer will have the same address as the dataPtr pointer.

5.4. Arrays

JNIWrapper supports two types of arrays:

  1. Primitive arrays that are made up of primitive values, such as integers or characters.

  2. Complex arrays that can consist of elements of any implemented type.

You can use complex arrays to store primitive values, but the primitive arrays are more efficient for this purpose.

Arrays are represented by the instance of com.jniwrapper.PrimitiveArray and com.jniwrapper.ComplexArray for the primitive and complex arrays, respectively. Arrays are represented by PrimitiveArray and ComplexArray types parametrized by parameters that represent the actual type. For example:

int i[10];  

is

PrimitiveArray i = new PrimitiveArray(Int.class, 10);

The main difference between the two types of arrays is that a primitive array is a plain data block that contains sequential data of a given type, while a complex array is a sequential storage of elements of any complexity that are all read and written whenever an array is read or written, respectively. This means that an array of pointers cannot be implemented as a primitive array because the referenced objects will not be written or read when needed. PrimitiveArray can be only of primitive types (int, char, float) and not of arrays, pointers, structures, etc. ComplexArray has no restriction on its element type.

Sometimes arrays can be specified as pointers in function signature, for example:

void foo(int *arg);

But if the actual value is a pointer to some number of integers, use array as a parameter.

The simplest method of creating an array is using a constructor specifying a sample parameter and array size. Note that the array's member type should be correctly cloneable. For example, to create an array of bytes, you can use the following construct:

PrimitiveArray val = new PrimitiveArray(new Int8(), 256);

Another method of creating an array is using a Java array of parameters that should constitute it. This is achieved by using a constructor taking a Parameter[] argument. Both primitive and complex arrays can be created this way. Here is an example of creating an array of pointers to integers:

Parameter[] members = new Parameter[10];
for(int i = 0; i < 10; i++) {
        members[i] = new Pointer(new Int(i));
}
ComplexArray result = new ComplexArray(members);

5.4.1. Pointers to Array Contents

When using arrays you should always remember that sometimes arrays are stored or passed to functions not as plain data, but as a pointer to the array contents. The most typical case is when an array argument or member is defined as a pointer to type (e.g. double*). In this case, the actual passed parameter should be a Pointer. For example:

/*
C declaration:
struct s
{
        int size;
        double *data;
}
*/
Int intMember = new Int(50);
PrimitiveArray arrayMember = new PrimitiveArray(DoubleFloat.class, 50);
Structure s = new Structure(new Parameter[] {intMember,
        new Pointer(arrayMember)});

If in the previous example, the second member is declared as double data[50] (predefined size array), the pointer wrapper should not be used.

The same rule applies for passing arrays to a function call. For example:

 /*
 C declaration:
 double foo(double* data, int elementsCount);
 */

 Function foo = library.getFunction("foo");
 DoubleFloat result = new DoubleFloat();
 int count = 50;
 PrimitiveArray array = new PrimitiveArray(DoubleFloat.class, count);
 foo.invoke(result, new Pointer(array), new Int(count); 

5.4.2. Controlling Memory Allocation for Arrays

In most cases, the array sizes are known or may be computed before a call is made. There are cases, however, when defining an array size before a function call is not possible or not efficient. In these cases, the array that is passed by a pointer is either resized to accommodate all the data or allocated altogether by the callee. In the first case, the caller is usually still responsible for memory deallocation, while in the second case, the memory management is most likely the responsibility of the callee. The common thing in both cases is that the called function returns the new size of the array as one of the results of the call.

JNIWrapper supports both ways of required memory management by using special array pointers - ResizingPointer and ExternalArrayPointer. Each of these pointers does not read the array it points to after the function call. The array should be read after the call is complete using the readArray(int count) method of the pointer. For example:

PrimitiveArray myArray = new PrimitiveArray(Int8.class, length);

Int16 len = new Int16(length);
ResizingPointer pArray = new ResizingPointer( myArray);;
Function func = getFunction("myFunction");
func.invoke(null, pArray, new Pointer(len));

length = (short)len.getValue();
pArray.readArray(length);
// use myArray here

The rule of thumb for choosing the correct pointer is as follows: if you want JNIWrapper to manage the memory allocated for the array, use ResizingPointer; if you do not need the memory management for the array memory, use ExternalArrayPointer.

5.5. Strings

Strings in JNIWrapper are character arrays of a predefined size with convenience methods for getting and setting string values as zero terminated strings. JNIWrapper supports two types of strings: single-byte, or ANSI strings, and Unicode strings.

All strings are defined with the maximum length that a string value can occupy. It is illegal to set a string parameter value to the value longer than its maximum length. Passing a string argument to a function that writes more data than is allocated may result in an error just like in the native programs. On the other hand, strings are safe in the way that the data is parsed until the terminating zero (of appropriate length) is found or the maximum string length is reached. Therefore, even a bad string without a terminating zero will not cause memory access failures in your program. Here are the examples of string usage:

AnsiString s = new AnsiString("Hello, World!");
WideString s2 = new WideString(20);
s2.setValue("Goodbye, World!");

Similar to arrays, strings in native code can be both pointers to string data and character arrays themselves. When using strings as structure members, use the same guidelines to determine whether a pointer wrapper should be used. When passing strings as function arguments, however, strings are always passed as pointers and JNIWrapper does pointer wrapping automatically. You should remember though that wrapping is just a convenience feature and passing a char** will require creating a new Pointer(new Pointer(new AnsiString())).

5.5.1. Str Class

The Str class unifies different ways of working with native string data. Instances of the class work with ANSI or Unicode characters depending on the Unicode support available in the underlying operating system. If Unicode is supported, this class works as WideString, otherwise as AnsiString. You can also create Str instances that work specifically with ANSI or Unicode. Using this class would give the code that is easier to read, refactor and maintain.

5.5.2. StringArray Class

The StringArray class provides functionality for working with native double zero terminated string data. When not explicitly defined, the type of characters depends on the Unicode support of an operating system under which the code is being executed. To set the required character type, use the StringArray(boolean unicode) constructor.

5.6. Enumerations

A C enumeration type (for example enum a {first, second, third}) can be substituted by an integer parameter, for example Int class (or any other integer parameter). In this case, new Int(0) corresponds to 'first', new Int(1) corresponds to 'second' and so on.

Chapter 6. Calling Native Functions


Native functions are called using the invoke() method of the Function class. The arguments and return value are specified using variables of the Parameter type. There are several overloaded versions of the invoke() method, but the idea and argument structure is similar: the first argument is always a holder for the return value and the rest are arguments in the order they appear in the function declaration.

When a function is called, all passed parameters are passed to it and then the return value is stored in its placeholder. It is allowed to pass null instead of the return value parameter, in which case it will be ignored, but it is allowed for primitive types only. Doing this when the return value is not of a primitive type may result in an error, because memory must be allocated by the function caller if the function return value is big and there is no way to allocate appropriate structure automatically without knowing the actual return value type. There are no restrictions on the argument or return value types as long as they are what a function expects.

6.1. Calling Conventions

If you are going to use a calling convention that differs from the default one used by JNIWrapper, the calling convention must be set before a native function is first called. Failure to do so will result in an error. Here is a complete example of calling a function:

Function sprintf =
        new Library("msvcrt").getFunction("sprintf");
sprintf.setCallingConvention(
        Function.CDECL_CALLING_CONVENTION);
AnsiString result_buffer = new AnsiString();
sprintf.invoke(
        null,
        result_buffer,
        new AnsiString("Hello, %s!"),
        new AnsiString("World"));
System.out.println("result = " + result_buffer.getValue());
//Output: result = Hello, World!

This example shows that there is no problem with calling functions even with variable argument number.

JNIWrapper checks that stack is left in consistent state after the function is called and handles most of the failures by throwing a Java exception.

All exceptions descend from com.jniwrapper.FunctionExecutionException.

These error or information messages from JNIWrapper can be disabled by adding the special category for JNIWrapper classes to your log4j configuration file:

log4j.category.com.jniwrapper=FATAL

Also, it can be configured programmatically:

Category jniwrapper = Category.getInstance("com.jniwrapper");
jniwrapper.setLevel(Level.FATAL);

6.1.1. A Quick Way to Call a Function

The Function class provides a shortcut way to call a function from a library without creating Library and Function instances: the call() method. To use it, write the following:

Table 6-1.

Function.call("user32.dll", "MessageBeep", retVal, new UInt(1));              
Function.call("/lib/libc.so.6", "printf", null, new AnsiString("Hello, World!\n"));
Function.call("/usr/lib/libSystem.dylib", "printf", null, new AnsiString("Hello, World!\n"));

This will load a library using the default loader, look up a function and invoke it using the default calling convention.

It is not recommended to use the call() method when your program makes a lot of native function calls, because it is more resource intensive. However, it is perfectly fine to use it in simple cases like getting the current directory.

6.2. Using Callbacks

Callback is a user-defined function that is called by the library code at the appropriate time. Callbacks can have arguments and return a value. JNIWrapper supports callbacks with any kind of arguments and return values, as well as stdcall and cdecl calling conventions. Callbacks are represented by subclasses of the com.jniwrapper.Callback class.

Passing a callback as an argument is no different from passing any other value: you just need to put an instance of the Callback class as the corresponding argument. For example:

Table 6-2.

final class EnumWindowsProc extends Callback
// ...
EnumWindowsProc enumWindowsProc = new EnumWindowsProc();
Function.call("user32.dll", "EnumWindows", retVal,
enumWindowsProc, new Int32(57));             
class MyComparator extends Callback
// ...
Function.call("/lib/libc.so.6", "qsort", null,
dataPointer, new Int(size), new Int(memberLen), new MyComparator()); 
class MyComparator extends Callback
// ...
Function.call("/usr/lib/libSystem.dylib", "qsort", null,
dataPointer, new Int(size), new Int(memberLen), new MyComparator()); 

When a callback is no longer needed, you should explicitly free the resources associated with it by invoking its dispose() method. This is the only case in JNIWrapper where resources are to be explicitly freed, because some types of the callback objects may have no references from code and still be active, for example a window procedure callback instance may be created once when the window class is registered and remain active during the entire program lifecycle.

To implement a callback, you need to subclass the Callback class implementing the callback() abstract method. The callback arguments and return value are specified in the constructor (by calling either the superclass constructor with parameters or init() method). The order of parameters is following: first go the arguments of a callback, and then a return value. Calling convention should also be set in the constructor if it is different from the default one .

When the callback() method is invoked, argument parameters contain the values of the passed arguments; after the callback() method is complete, the value of the return value parameter is returned to the caller. Here is an example of a callback that takes an integer argument and returns its value incremented by one:

/*
C callback declaration:
int callback(int);
*/
private static class IncIntCallback extends Callback {
    private Int _arg = new Int();
    private Int _result = new Int();

    public IncIntCallback() {
        init(new Parameter[] {_arg}, _result);
    }

    public void callback() {
        _result.setValue(_arg.getValue() + 1);
    }
}

Chapter 7. Working with Multithreading


JNIWrapper was designed with threading in mind and is already successfully used in a multithreaded environment. The following sections describe JNIWrapper components in terms of thread safety.

7.1. Parameters

Parameters in JNIWrapper are not synchronized because synchronizing at this level is very inefficient, and performance was a high-priority goal in JNIWrapper development. Therefore, access to the parameter data should be enclosed in synchronization blocks if there is a possibility for more than one thread to access the data at a time. If you have only one parameter that is shared between threads, you can synchronize on that object's monitor:

Int32 sharedInt = new Int32();
// ...
// First thread
synchronized(sharedInt) {
    someFunction.invoke(sharedInt);
}
// ...
// Second thread
synchronized(sharedInt) {
    if (sharedInt.getValue() == 10)
        System.out.println("Value is set to 10");
// ...

7.2. Functions

Function invocation in JNIWrapper is completely thread-safe. If the called function is thread-safe, any number of threads may invoke it concurrently at any time. No synchronization is required and/or performed, and calls are executed fully concurrently as if invoking simple Java methods.

Chapter 8. Code Generator for JNIWrapper


In the new version of JNIWrapper 3.0 we have added a new Code Generator application. Its main goal is to provide a convenient and efficient way to generate Java wrappers for custom C types such as structures, unions, callbacks, etc.

8.1. Overview

The Code Generator for JNIWrapper allows you to select C code containing the required C types and generate the corresponding Java wrappers for them. This code can be either in a C header file or in a fragment of code, which can be inserted from the clipboard or just typed as text. The resulting Java classes (Java wrappers for C types) are saved to the specified folder.

8.2. Running Code Generator for JNIWrapper

The application can be started by the appropriate file from the bin directory of JNIWrapper installation. The table below shows what file should be executed (depending on the platform) to start the applicaiton:

Table 8-1.

Codegen.bat
Codegen-linux.sh
Codegen-macosx.sh

This application does not provide command-line mode.

8.3. Specifying Input Parameters and Options

The application provides an ability to specify various options for code generation. These options can be divided into the following three categories:

  • Input parameters specify the input C source code in which the necessary types are defined. It can be a C header file, a folder that contains C headers files or a fragment of C code.

  • Generation parameters specify the set of types to generate wrappers for, their names and base types respectively.

  • Output parameters specify the destination folder to save all generated Java files to and the root package name for all generated classes.

8.4. Using Type Replacement Table

The Code Generator application imports all types that are defined in the specified source and builds a correspondence table: every imported C type is mapped to the corresponding type from the JNIWrapper library. The application provides an ability to change the name of a particular type as well as its base type (the parent type from the JNIWrapper library), in case this type was not recognized by the C parser.

Also, it is possible to define a set of types to be generated: you can just select the necessary types in the appropriate table.

8.5. Namespaces and Package Naming

The destination root package (which can be specified in the options) is actually a root for other subpackages automatically generated by the application. Also, it is possible to give a custom package name to each type (resulting class) individually.

8.6. Supported Types

The current version of the application supports the following C types:

  • struct

  • union

  • callback

  • enum

  • types that are defined or redefined using the typedef keyword

Also, the application supports generation of pointers and arrays.

8.7. Known Limitations

The current version of the application does not recognize the following C preprocessor directives:

  • #include

  • #define

  • #if

  • #error

The inline functions (for example "void GetData() { return;}" should not be present in the C code either.

Nester pointer types (for example: typedef int** PPInt) are not supported yet and they are processed as usual single pointers.

Also, please keep in mind that the application just parses the given C code, but does not validate it anyhow. Therefore, before giving some C code to this application, make sure that code is compilable and valid.

To generate a more complete set of wrappers, we recommend you to precompile the input C code with a C-compiler first, and then give the resulting file to the Code Generator application. This step will help you eliminate all unresolved dependencies.

8.8. Example

Suppose you need to create a wrapper for the following C structure:

struct Sample {
  int a;
  float b;
};

which is used in the following C function:

void sample(Sample* structure);

The Code Generator application will generate the following Sample.java wrapper for this C structure:

public class Sample extends Structure {
  private Int _a = new Int();
  private SingleFloat _b = new SingleFloat();
  public Sample() {
    init(new Parameter[] {_a, _b});
  }
  ...
}

Then, this generated Sample.java file should be included to the project and compiled. And the function call can be as follows:

Library lib = ...; // library that contains 'sample' function
Function sample = lib.getFunction("sample");
Sample sample = new Sample();
function.invoke(null, new Pointer(sample));

Chapter 9. Using AWT Native Interface


JavaTM includes the cross-platform standard windowing library called Abstract Window Toolkit(AWT). One of the design principles of the AWT was to include only the features that can be implemented on all platforms targeted for JavaTM. This imposed some limitations on the windowing interface features. To help you access the native controls that stand behind the AWT, the AWT native interface (JAWT) was introduced. JNIWrapper also supports this interface so that you don't need to write native code to access the JAWT features.

9.1. Using JAWT Support

On the native side, all the JAWT functionality can be accessed through several structures defined in the include/jawt.h file in the JDK directory. The root structure - JAWT - is available from the GetAWT function. JNIWrapper provides the com.jniwrapper.jawt.JAWT class that implements all the functionality available through the JAWT structure. The JAWT_DrawingSurface and JAWT_DrawingSurfaceInfo classes correspond to the structures with the same names in the jawt.h file and provide the same functionality.

9.2. Accessing Native Control's Data

The most common use of the JAWT interface is to get a handle of the native control that corresponds to a given component. This data is returned in the platform-dependent structure pointed to by the platformInfo member of the jawt_DrawingSurfaceInfo structure. The contents of this structure are defined in the jawt_md.h file in the include/<platform>/ directory of the JDK installation. The correct structure needs to be passed to the constructor of the JAWT_DrawingSurfaceInfo class. A sample structure for Win32 is shown below:

public class Win32DSI extends Structure
{
    private Pointer.Void _handle = new Pointer.Void();
    private Pointer.Void _hdc = new Pointer.Void();
    private Pointer.Void _hpalette = new Pointer.Void();

    public Win32DSI()
    {
     init(new Parameter[]{_handle, _hdc, _hpalette},
            (short) 8);
    }
    /**
     * Returns target component handle (either window or
     * bitmap handle).
     */
    public Pointer.Void getHandle()
    {
        return _handle;
    }

    /**
     * Returns DC handle. This handle should be used for
     * drawing instead of handles returned * from
     * <code>GetDC</code> or <code>BeginPaint</code>.
     */
    public Pointer.Void getHdc()
    {
        return _hdc;
    }

    /**
     * Returns palette handle.
     */
    public Pointer.Void getHpalette()
    {
        return _hpalette;
    }
}

9.3. Getting HWND of a Window

Below is given an example of a code snippet that gets a Win32 window handle of an AWT window. It is very similar to the C-code required to do the same thing.

JAWT_DrawingSurface ds = JAWT.getDrawingSurface(window);
ds.lock();
Win32DSI win32DSI = new Win32DSI();
JAWT_DrawingSurfaceInfo dsi = new JAWT_DrawingSurfaceInfo(win32DSI);
Pointer pDsi = new Pointer(dsi);
ds.getDrawingSurfaceInfo(pDsi);
int result = (int) win32DSI.getHandle().getValue();
ds.freeDrawingSurfaceInfo(pDsi);
ds.unlock();
JAWT.freeDrawingSurface(ds);
return result;

In the table below you can see how to create the wrappers for Java windows on different platforms:

Table 9-1.

// Using Wnd class from WinPack library
JFrame window = new JFrame();
window.setVisible(true); //a Java window should be visible
Wnd wnd = new Wnd(window);
// Using NSWindow class from MacPack library
JFrame window = new JFrame();
window.setVisible(true); // a Java window should be visible
NSWindow wnd = new NSWindow(window);

9.4. JAWT Support in Different JDK Versions

When programming using the JAWT interface, you should keep in mind that JAWT is a relatively new technology. It was introduced only in JDK 1.3 with limited functionality, has had many improvements in JDK 1.4, and JAWT is intended to completely replace such native-related functions as sun.awt.windows. WToolkit.getNativeWindowHandleFromComponent. For JDK 1.3, you should expect very limited support for JAWT. For example, the argument of the getDrawingSurface() method can only be a java.awt.Canvas. JDK 1.4 introduced new functions, such as AWT locking and unlocking, as well as extended the capabilities of the existing functions.

Chapter 10. Using JNIWrapper in Java Web Start Applications


This section describes the way of deploying your applications that use JNIWrapper with the help of Java Web Start (JWS).

One of the major requirements for any JWS application is that all its resources are located inside signed JAR files. Although JAR files can be signed multiple times, JWS does not accept files with more than one signature. It is also mandatory that all application JAR files are signed with the same signature.

All JNIWrapper libraries are supplied already signed and signing them with a new signature makes them unacceptable for JWS. Fortunately, there is a simple solution. The main idea is to use the <extension> tag in the .jnlp file and to create two different .jnlp files for your application. One .jnlp file should contain your application files and the other - JNIWrapper resources. This technique is demonstrated in the example below. The first file is the application .jnlp file (demo.jnlp):

<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0+" codebase="http://www.teamdev.com/" href="demo.jnlp">
  <information>
    <title>WinPack Demo Application</title>
    <vendor>TeamDev Ltd.</vendor>
    <description>WinPack Demo Application</description>
    <description kind="short">The demo of WinPack library</description>
    <offline-allowed/>
  </information>
  <security>
      <all-permissions/>
  </security>
  <resources>
    <j2se version="1.3+" initial-heap-size="64m"/>
    <property name="sun.java2d.noddraw" value="true"/>
    <jar href="demo.jar"/><!-- demo.jar is your jar file signed with your own signature-->
    <extension name="jnw" href="jnw.jnlp"/>
  </resources>
  <component-desc/>
  <application-desc main-class="com.jniwrapper.win32.samples.demo.WinPackSet"/>
</jnlp>

The <extension> tag above makes the reference to the other jnw.jnlp file which is declared in the following way:

<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0+" codebase="http://www.teamdev.com/" href="jnw.jnlp">
  <information>
    <title>JNIWrapper resources</title>
    <vendor>TeamDev Ltd.</vendor>
    <description>JNIWrapper Application</description>
    <description kind="short">JNIWrapper Application</description>
    <offline-allowed/>
  </information>
  <security>
      <all-permissions/>
  </security>
  <resources os="Windows">
    <nativelib href="jniwraplib.jar"/>
  </resources>
  <resources>
    <jar href="jniwrap.jar"/>
    <jar href="winpack.jar"/>
  </resources>
  <component-desc/>
</jnlp>

The second jnw.jnlp file represents the JNIWrapper resource bundle for redistribution as part of another JWS application. The jniwraplib.jar package should only include one file: jniwrap.dll.

After you've configured the .jnlp files, place them to your Web site and create a link to your main .jnlp file that will also download JNIWrapper resources by the reference.

Important

The license file for JNIWrapper should be placed to the META-INF folder of your application JAR file.

Chapter 11. Using JNIWrapper in Applets


To use JNIWrapper in applets, follow these instructions:

  1. Copy jniwrap.dll to the folder of a web server computer where the applet resides (it is recommended, but you can place jniwrap.dll to some other folder on the web server).

  2. Prepend the following code to the init() method of the applet:

    // passes the instance of the current object as parameter
    AppletHelper.getInstance().init(this); 

    This call downloads jniwrap.dll from the web server and copies it to the Windows system32 folder in the client's computer. The presence of jniwrap.dll on the client side is required, otherwise the applet will not work.

    If you copied jniwrap.dll to a folder with no residing applet, you should provide JNIWrapper library URL, for example:

    AppletHelper.getInstance().init("http://applets.com/native/jniwrap.dll");
  3. Prepend the following code to the method to start the applet method:

    AppletHelper.getInstance().start();

    This call starts NativeResourceCollector thread of JNIWrapper.

  4. Append the following code to the method to stop the applet method:

    AppletHelper.getInstance().stop();

    This call stops NativeResourceCollector thread of JNIWrapper.

  5. The license file (jniwrap.lic) can be included into any JAR file of a JWS application, into the META-INF subfolder.

  6. application.jar should be signed and should reference the JNIWrapper library(s) in its manifest file by setting the class path variable. Signing JNIWrapper libraries is not necessary as they are already signed.

The sample build file that prepares an applet library is shown below:

<project name="Applet Sample" default="build" basedir=".">
 <property name="certificate" value=".keystore"/>
 <property name="licenseFile" value="jniwrap.lic"/>
 <property name="appletClasses" value="classes"/>
 <target name="build">
  <jar destfile="sample.jar">
   <fileset dir="${appletClasses}" includes="AppletSample.class"/>
   <manifest>
    <attribute name="Class-Path" value="jniwrap-2.5.jar"/>
   </manifest>
   <zipfileset file="${licenseFile}" prefix="META-INF" />
  </jar>
  <signjar jar="sample.jar" alias="your_alias" keystore="${certificate}"
                    storepass="your_storepass" keypass="your_keypass" />
 </target>
</project>

Below is given the applet usage sample:

<html>
<body><h1>Applet Sample</h1>
<p>
 <applet docbase="http://your_url" code="AppletSample.class" width="400" height="300" archive="sample.jar"/>
</body>
</html>

Chapter 12. Using JNIWrapper in Native Executable Archives


To demonstrate how to make a native executable file (which uses JNIWrapper) using JBuilderX "Archive Builder" utility, we have prepared the following algorithm:

  1. Remove all the signatures from JNIWrapper libraries you are going to use in your application. To do this, delete JNIWRAPP.DSA and JNIWRAPP.SF files from the META-INF subfolder in JNIWrapper JAR-libraries (such as jniwrapper-3.0.jar). This is necessary because "Archive Builder" does not correctly copy JAR's signatures and manifest files to the resulting archive.

  2. Copy the JNIWrapper runtime license file (jniwrap.lic) to the META-INF subfolder of the JNIWrapper library (for instance, jniwrapper-3.0.jar\META-INF\).

  3. Select the "Always include all classes and resources" option in the "Dependencies" section. This is necessary because "Archive Builder" does not correctly track the dependencies.

  4. Make an executable library.

To run the created executable file, you need to place the jniwrap.dll library in the same folder.

Chapter 13. Using JNIWrapper in Servlets


If you're going to use JNIWrapper in a web application, just place the jniwrap.dll, jniwrap.lic and jniwrap.jar files in the appropriate web server catalog within the program library search path. For example, if you use the Tomcat web server, you can place the files in:

$TOMCAT_HOME/webapps/MyApp/WEB-INF/lib

but not in

$TOMCAT_HOME/common/lib

Also, in a servlet you can add WEB-INF/lib directory to the path using the following code:

DefaultLibraryLoader.getInstance().addPath(this.getServletContext().getRealPath("/") + "WEB-INF/lib");

Chapter 14. Support


If you have any problems or questions regarding JNIWrapper, please check the documents listed below. The answer to your question may already be there:

If none of the above resources contain the required information, please e-mail us at:

jniwrapper-support@teamdev.com

14.1. Reporting Problems

If you find any bugs, please submit the issue to us using a special report form on the TeamDev integrated customer support and troubleshooting center at:

http://support.teamdev.com/forms/reportForm.jsf

The form will help you provide all necessary information.

14.2. Troubleshooting

To find a solution to your problem, please visit the Troubleshooting page at:

http://www.teamdev.com/jniwrapper/tshoot.jsf

This page is regularly updated using information from support requests.

If you didn't find a solution, please e-mail us at jniwrapper-support@teamdev.com or report the problem as described in the previous section.

Chapter 15. Where to Get a New Version


To get the latest version of JNIWrapper, please visit:

http://www.teamdev.com/jniwrapper/downloads.jsf

Copyright © 2002-2021 TeamDev Ltd.