Simplify Native Code Access with JNA Blog


    This article describes the Java Native Access (JNA) approach to integrating native libraries with Java programs. It shows how JNA enables Java code to call native functions without requiring glue code in another language. The examples illustrate usage patterns, common pitfalls, and troubleshooting techniques. The article also enables a comparison of JNA and JNI (Java Native Interface) by describing the conversion of sample JNI code from an earlier article to JNA.

    It is useful to know JNA because the Java APIs, with their architecture-neutral emphasis, will never support platform-specific functionality. So, for example, if that killer app you've just invented needs to play the Windows "Critical Stop" sound you'll be stuck as the Windows MessageBeep()function can't be called via the standard APIs.

    Though Java itself is architecture-neutral, the example code used in this article is, perforce, platform-specific. The code has been developed and tested on a Laptop PC running 32-bit Microsoft Windows XP and Sun JRE 1.6.0 update 16. However, the code is quite generic and should run on a range of Windows and JVM versions. Features new in Java 1.6, Windows 2008, and Windows Vista have not been used.

    JNA Development First Steps

    Here are a few things you have to take care of when starting a JNA project:

    • Download jna.jar from the JNA project site and add it to your project's build path. This file is the only JNA resource you need. Remember that jna.jar must also be included in the run-time classpath.

    • Find the names of the DLLs that your Java code will access. The DLL names are required to initialize JNA's linkagemechanisms.

    • Create Java interfaces to represent the DLLs your application will access. The sample code accompanying this article contains example interfaces for three DLLs: kernel32.dll,user32.dll, and Twain_32.dll.

    • Test linkage of your Java code to the native functions. The first example below, "Linkage: What's in a Name?", describes the exceptions to expect when JNA can't find a DLL or a function in a DLL.

    If your project is large or complex, it may be a good idea to complete these steps in an early phase. If a proof of concept (POC) is required, consider including a significant portion of JNA interface code in the POC. This helps to validate assumptions about JNA's suitability for the job, and reduces overall project risk.

    A Proxy for the DLL

    JNA uses the proxy patternto hide the complexity of native code integration. It provides a factory method that Java programs use to obtain a proxy object for a DLL. The programs can then invoke the DLL's functions by calling corresponding methods of the proxy object. The sequence diagram in Figure 1 below depicts the creation and use of a proxy object.

    UML sequence diagram
    Figure 1. Creation of a Java proxy object for a DLL

    JNA takes care of all run-time aspects, but it requires your help to create the proxy's Java class. So the first piece of code you need to create is a Java interface with method definitions that match the DLL's C functions. To play with JNA's run-time correctly, the interface must extend com.sun.jna.Library. The code below shows an abbreviated view of a proxy interface for the Windows user32 DLL. Note that there should be one such Java interface for each DLL.

    package libs; import com.sun.jna.win32.Library; public interface User32 extends Library { ... (lines deleted for clarity) ... boolean 
    LockWorkStation(); boolean 
    MessageBeep(int uType); ... (lines deleted for clarity) ... }

    Many DLLs, such as those in the Windows API, host a large number of functions. But the proxy interface need only contain declarations for the methods your application actually uses.

    Linkage: What's in a Name?

    Our first example ( is extremely simple, and locks the workstation when it is run (same effect as pressing the Windows logo + Lkeys together). It uses the User32 interface shown above to create a proxy for the Windows user32 DLL. It then calls the proxy'sLockWorkStation() method -- which in turn invokes the DLL's LockWorkStation()function. The run-time mapping of the proxy method to the DLL function is handled transparently by JNA -- the user just has to ensure that the method name matches the function name exactly.

    import com.sun.jna.Native; // JNA infrastructure import libs.User32; // Proxy interface for user32.dll public class LockWorkStation { public static void main(String[] args) { // Create a proxy for user32.dll ... User32 user32 = (User32) Native.loadLibrary("user32", User32.class); // Invoke "LockWorkStation()" via the proxy ... user32.LockWorkStation(); } }

    To compile and run this program follow the instructions at "Running the Sample Code" below.

    The absence of parameters and a return value in theLockWorkStation() call eliminates the possibility of any programming errors. But there are still two things that can go wrong with code as simple as this:

    • loadLibrary() throws ajava.lang.UnsatisfiedLinkError with the message "Unable to load library ... The specified module could not be found." If this error occurs check the spelling of the DLL name, and verify that the DLL is in one of the searched directories (check JNA documentation).

    • A proxy method (such as LockWorkStation()) throws a java.lang.UnsatisfiedLinkError with the message "Error looking up function ... The specified procedure could not be found." If this error occurs check the spelling of the function name, and verify that the "function" is not actually amacro. The Windows API DLLs contain quite a few such macros (e.g. GetMessage(), defined in Winuser.h), so read the DLL's documentation (and the associated header files) carefully. Macro names must be translated manually.

    You shouldn't get either of these exceptions when But you can simulate these errors just by changing the name of a DLL or a function and recompiling the code. JNA does, in fact, have mechanisms to allow you to use a method name (in the proxy interface) that is different from the function name (in the DLL). More information on this feature can be found in the JNA documentation.

    Parameter and Return Types

    Our next example, shown below, uses the Windows Beep()function to literally beep "Hello world" in Morse code.

    import com.sun.jna.Native; // JNA infrastructure import libs.Kernel32; // Proxy interface for kernel32.dll public class BeepMorse { private static Kernel32 kernel32 = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class); private static void toMorseCode(String letter) throws Exception { for (byte b : letter.getBytes()) { kernel32.Beep(1200, ((b == '.') ? 50 : 150)); Thread.sleep(50); } } public static void main(String[] args) throws Exception { String helloWorld[][] = { {"....", ".", ".-..", ".-..", "---"}, // HELLO {".--", "---", ".-.", ".-..", "-.."} // WORLD }; for (String word[] : helloWorld) { for (String letter : word) { toMorseCode(letter); Thread.sleep(150); } Thread.sleep(350); } } }

    Beep() takes two arguments, frequency and duration, both of type DWORD which is definedas unsigned long. Since an unsigned longoccupies 32 bits in all current flavors of Windows, we use a Javaint for both arguments in the proxy interface definition shown below:

    package libs; import com.sun.jna.Library; public interface Kernel32 extends Library { // ... (lines deleted for clarity) ... boolean 
    Beep(int frequency, int duration); int 
    GetLogicalDrives(); // ... (lines deleted for clarity) ... }

    It is important to deduce the argument types correctly as you can verify by changing the type of Beep()'s arguments. Changing the definition to Beep(long, long) orBeep(float, float) does not cause any run-time error, but you will hear no sound at all. The JNA web-site has some information on translating Windows types to Java types. More details can be found at wikibooks' Windows Programming/Handles and Data Types page and Microsoft's Windows Data Types page.

    To compile and run this program follow the instructions at "Running the Sample Code" below, but remember to turn the volume down first!

    Beep() returns a boolean value, although it is ignored in this example. But if the value returned by a function has to be used, the return type must be mapped to a suitable Java type using the same guidelines as for parameter types. The code below ( illustrates the use of theint value returned by GetLogicalDrives()in the kernel32 DLL.

    import com.sun.jna.Native; import libs.Kernel32; public class GetLogicalDrives { public static void main(String[] args) { Kernel32 kernel32 = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class); int drives = kernel32.GetLogicalDrives(); for (int i = 0; i < 32; ++i) { int bit = (1 << i); if ((drives & bit) == 0) continue; System.out.printf("%c:\\%n", (char) ((int) 'A' + i)); } } }

    Note, however, that in practice a Java program should never have to call GetLogicalDrives() using JNA provides the same information.

    To compile and run this program follow the instructions at "Running the Sample Code" below.

    The article's introduction mentioned the use of Windows standard sounds for indicating specific events. These sounds can be produced by calling the MessageBeep(int type) function. Example code showing the use ofMessageBeep() can be found in the

    C structs in Java

    C Functions often use structs as arguments. But since Java does not have structs, JNA uses classes instead. Classes are closely related to structs, so the associated Java code looks intuitive, and works well. The following code extracted, the proxy interface for kernel32.dll, illustrates the conversion of struct SYSTEMTIME into a Java class to support the GetSystemTime()function.

    import com.sun.jna.Library; import com.sun.jna.Structure; public interface Kernel32 extends Library { // ... (other members deleted) ... public static class 
    SYSTEMTIME extends Structure { public short wYear; public short wMonth; public short wDayOfWeek; public short wDay; public short wHour; public short wMinute; public short wSecond; public short wMilliseconds; } void 
    GetSystemTime(SYSTEMTIME st); // ... (other members deleted) ... }

    Note that Java classes that substitute C structs must extend JNA's com.sun.jna.Structure base class. Embedding these classes inside the proxy interface helps to keep everything neatly organized in a single file. This is particularly effective when the struct is only used by functions in the same proxy interface. These classes can, however, also be defined as standalone public classes (outside the proxy interface) if that is required or preferred. The JNA web site has more information on these aspects.

    The code shown below, in the sample code, illustrates the use of structs. In this example the called function uses the struct to pass information "out", but structs can be used to pass information "in" (as in the Windows SetSystemTime()function) or "in and out" as well.

    import libs.Kernel32; import libs.Kernel32.SYSTEMTIME; import com.sun.jna.Native; public class GetSystemTime { public static void main(String[] args) { Kernel32 kernel32 = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class); SYSTEMTIME st = new SYSTEMTIME(); kernel32.GetSystemTime(st); System.out.printf("Year: %d%n", st.wYear); System.out.printf("Month: %d%n", st.wMonth); System.out.printf("Day: %d%n", st.wDay); System.out.printf("Hour: %d%n", st.wHour); System.out.printf("Minute: %d%n", st.wMinute); System.out.printf("Second: %d%n", st.wSecond); } }

    To compile and run this program follow the instructions at "Running the Sample Code" below.

    It is important to deduce the type of each member of a converted struct correctly. Erring here usually has catastrophic consequences that you can sample by changing the types inSYSTEMTIME. There are other JNA tweaks that can be applied to specify whether a struct should be passed by reference (the default) or by value, and also how a struct embedded within another should be stored. The JNA web-site has much guidance on these aspects. The section titled "Converting from JNI to JNA" below has several examples of the conversion of C structs to Java classes.

    No discussion of struct portability across languages is complete without also considering memory alignment requirements. Since this part of the article is dedicated to JNA basics we defer discussion of alignment requirements to a later section "Converting from JNI to JNA".

    Pointers and Strings

    Using pointers is a perfectly natural thing to do in C, C++, and certain other languages. But the use of pointers also proliferated certain errors and programming malpractices that Java's inventors wanted to prevent. So, although Java programs have an uncanny resemblance to C++ code, Java has no pointers. But pointers of one kind or another are commonly used as parameters in native functions, so JNA programs must be creative in working around this limitation.

    The following example ( exploits a language feature from Java's C heritage: an array reference is a pointer to the array's first element.

    import libs.Kernel32; import com.sun.jna.Native; public class GetVolumeInformation { private static String b2s(byte b[]) { // Converts C string to Java String int len = 0; while (b[len] != 0) ++len; return new String(b, 0, len); } public static void main(String[] args) { Kernel32 kernel32 = (Kernel32) Native.loadLibrary( "kernel32", Kernel32.class); int drives = kernel32.GetLogicalDrives(); for (int i = 0; i < 32; ++i) { if ((drives & (1 << i)) == 0) continue; String path = String.format("%c:\\", (char) ((int) 'A' + i)); byte volName[] = new byte[256], fsName[] = new byte[256]; 
    int volSerNbr[] = new int[1], maxCompLen[] = new int[1], fileSysFlags[] = new int[1]; boolean ok = kernel32.GetVolumeInformationA(path, volName, 256, 
    fileSysFlags, fsName, 256); if (ok) System.out.printf("%s %08X '%s' %s %08X%n", path, volSerNbr[0], b2s(volName), b2s(fsName), fileSysFlags[0]); else System.out.printf("%s (Offline)%n", path); } } }

    GetVolumeInformation()'s specificationstates that its 4th thru 6th arguments (highlighted above) are of type LPDWORD which translates to "pointer to int". We circumvent Java's lack of pointers by using int arrays for these arguments instead. So in the proxy's method declaration these arguments are defined to be of type int[], and at run-time (see code above) we pass int arrays of one element. The values returned by GetVolumeInformation() are left in the single int that populates each array.

    The output from this program is shown below. On my computerD: is a CD-ROM drive that was not loaded at the time this output was captured. The device at G: was a USB flash drive.

    C:\ 609260D7 'My-C-Drive' NTFS 000700FF D:\ (Offline) E:\ C8BCF084 'My-E-Drive' NTFS 000700FF G:\ 634BE81B 'SDG-4GB-DRV' FAT32 00000006

    To compile and run this program follow the instructions at "Running the Sample Code" below.

    Another thing to notice in the above code is the way strings are passed to and from native code. Java Strings can be passed "in" to the native code without special effort (check variable path in code above). But null-terminated strings passed "out" to Java require careful handling. Check the use of the variables volName andfsName, and the method b2s(byte b[]) in the code above. Finally, note that GetVolumeInformation()is a macro whose "real" name isGetVolumeInformationA(). Read the function's specification for all the details.

    Another approach to pointers in Java is based on the classes in the package com.sun.jna.ptr and the classcom.sun.jna.Pointer. Examples of the use of these classes can be found in the code discussed under "Converting from JNI to JNA" below.

    Converting from JNI to JNA

    Having covered the basics, it's now time to pit your wits against something more substantial. The rest of this article describes issues faced in converting an existing application (based on JNI) to JNA, Reviewing the converted code (included with the sample code) should provide greater insight into how JNA can be used to handle the complexities of a "real" application. The JNI code used comes from the article "Java Tech: Acquire Images with TWAIN and SANE, Part 1", which describes how the TWAIN library is used to obtain images from scanners webcams, and other imaging devices.

    To run the TWAIN code you should ideally have a TWAIN device (scanner, webcam, etc.) connected to your computer. But if your computer does not have a TWAIN device, you should download and install the TWAIN Developer Toolkit which contains a program that simulates an image source. To understand the code you should also have the TWAIN header fileavailable.

    To run the TWAIN demo program execute JTwainDemo.bat as described at "Running the Sample Code" below. To understand the overall flow of the program, follow the instructions starting at Let There Be TWAIN in the original JNI article.

    Figure 2 below depicts the changes that have been made to the sample code from the JNI article.

    Sample code structure
    Figure 2. Changes to "" from the JNI article

    jtwain.cpp and twain.h have been deleted as they contained only JNI-specific code. has been deleted as it was unrelated to TWAIN or JNI. has been modified to contain a JNA implementation of the TWAIN functionality instead of the original JNA code. The packagelibs is new. Its three files (,, and are the proxy interfaces discussed in this article. The remaining 3 files stay unchanged. Observe that the package democode, containing the simple example programs described above, is not shown in Figure 2.

    Converting the TWAIN code to JNA provides the usual learning experiences of any non-trivial project. But it also throws up a rare and elusive type of bug -- struct memory alignment error -- that is unique to Java projects using native-code. Since memory alignment errors are difficult to detect, and they may also be new to many Java users, the following sections provide a detailed guide to handling these errors.

    Detecting a Struct Memory Alignment Problem

    The devil is in the details here, so there can no simple, non-intrusive, way of concluding that a particular bug is caused by a memory alignment mismatch. But the following, necessarily tedious, discussion describes one approach. The affected code (shown below) is in, and can be located by searching for "Memory Alignment Problem Demo". The code creates an instance of TW_IDENTITY (a Java substitute for a struct), and passes it to the TWAIN run-time. TWAIN then interacts with the user to select a source device. TheTW_IDENTITY instance is uninitialized when passed from Java to TWAIN, but is returned populated with information about the selected device. The printf()s, and thedump() at the end of the code display parts of the struct to help in detecting the problem.

    // BEGIN: Memory Alignment Problem Demo TW_IDENTITY srcID = new TW_IDENTITY(Structure.ALIGN_DEFAULT); stat = SelectSource(g_AppID, srcID); if (stat != TWRC_SUCCESS) { //... (lines deleted for clarity) ... } System.out.printf("ProtocolMajor: %02x%n", srcID.ProtocolMajor); System.out.printf("ProtocolMinor: %02x%n", srcID.ProtocolMinor); System.out.printf("SupportedGroups: %04x%n", srcID.SupportedGroups); System.out.printf("Manufacturer: %s%n", new String(srcID.Manufacturer, 0, 34)); dump(srcID); // END: Memory Alignment Problem Demo

    The output from the printf() statements shown below give a strong hint that a memory alignment problem exists:

    ProtocolMajor: 01 ProtocolMinor: 09 SupportedGroups: 694d0000 Manufacturer: crosoft Tw

    The first two values, ProtocolMajor andProtocolMinor, are correct but the next two are certainly corrupted. In a previous TWAIN call, the Java code negotiated the value 0x0003 for SupportedGroups, so that same value should have been returned. Also the value ofManufacturer certainly looks like "Microsoft" with the first two characters lopped off.

    Now let's look at the output from dump() shown below. The "dump" displays the contents of the struct as received from the native code, before separation by JNA into individual member values.

    11 04 00 00 
    01 00 
    00 00 
    0d 00 
    01 00 
    32 36 20 4a 000: 
    . . . . 
    . . 
    . . 
    . . 
    . . 
    2 6 J 016: 
    75 6e 65 20 32 30 30 30 00 00 00 00 00 00 00 00 016: 
    u n e 2 0 0 0 . . . . . . . . 032: 
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 
    01 00 032: 
    . . . . . . . . . . . . . . 
    . . 048: 
    09 00 
    03 00 00 00 
    4d 69 63 72 6f 73 6f 66 74 00 048: 
    . . 
    . . . . 
    M i c r o s o f t . 064: 
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 064: 
    . . . . . . . . . . . . . . . . 080: 
    00 00 00 00 00 00 00 00 
    54 77 61 69 6e 20 44 61 080: 
    . . . . . . . . 
    T w a i n D a ... (other lines deleted for clarity) ...

    Each line of the dump displays the values of 16 bytes of "raw" memory. The number at the beginning of each line (before the colon) is the line's offset from the start of the struct. Each set of 16 bytes is printed twice -- first as hexadecimal integers, and then as ASCII characters. The colors (assigned manually) serve to delimit adjacent members of the struct, and the underlined part is thestruct TW_VERSION embedded within struct TW_IDENTITY (see The location and extent of each member in the struct's memory space is determined from the declaration order and size of each member.

    Looking at the dump above, it is obvious that the information returned by the native code is correct (remember that the PC's Intel processor is little endian). Specifically, the values of SupportedGroups andManufacturer are also correct in the dump:

    • ProtocolMajor (the 2 magenta bytes at offset 46) = 0x01
    • ProtocolMinor (the 2 cyan bytes at offset 48) = 0x09
    • SupportedGroups (the 4 magenta bytes at offset 50) = 0x0003
    • Manufacturer (the 34 cyan bytes at offset 54) = "Microsoft" (padded out to 34 bytes with 0-valued bytes)

    Comparison of the values from the printf()statements and those in the dump shows that JNA's sense of struct-member location has, mysteriously, slipped by 2 bytes starting at SupportedGroups. This is the classic symptom of a memory alignment issue.

    The alignment error occurs because the native code strings together the values of the struct's members without any intervening gaps, whereas the JNA code expects to find them at memory offsets that are multiples of the member's length. Thus, the native code places SupportedGroups at offset 50, but JNA looks for it at offset 52 (a multiple of 4, the size ofSupportedGroups). The struct members followingSupportedGroups also get pushed back by 2 bytes, leading to the corruption of Manufacturer's value shown above. You should now also be able to explain how the "Tw" creeps in at the end of Manufacturer's value.

    Finally, a short digression on another aspect of pointers: the code of dump() shows howStructure.getPointer() can be used to get a pointer to the beginning of a struct. The com.sun.jna.Pointerobject returned by getPointer() can be used to access the struct as an array of bytes (a C programmer'svoid*).

    Reproducing the Struct Alignment Error

    The file actually contains the code with the memory alignment error so that readers may explore this further if they wish. But the TWAIN demo program still works correctly as it does not use the values in the struct.

    To reproduce the memory alignment error compile the program as described at "Running the Sample Code" below, then executeJTwainDemo.bat. You should see the window titled "JTwain Demo" in Figure 3 below. At the menu bar select "File" -> "Select Source..." as shown in the figure. The window titled "Select Source" will pop up with a list of the installed TWAIN devices. Choose any TWAIN device, and click the button labelled "Select". This executes the code with the alignment error, and displays the contents of the struct TW_VERSION in the command window.

    Program output
    Figure 3. Running JTwainDemo

    Note that the struct TW_VERSION contents you see will likely differ from the example values shown above (unless you have the same TWAIN device installed). But you should be able to see the same kind of evidence of a memory alignment problem.

    If the pop-up window titled "Select Source" displays no TWAIN devices, you should download and install the TWAIN developer toolkit. The toolkit simulates an image source (the first entry in the "Select Source" window in Figure 3 above) that returns an image of the TWAIN logo.

    Preventing Struct Alignment Errors

    Native libraries come in various memory alignment flavors (because of differences between compilers and compiler options). So, since JNA is typically used in situations where re-compiling the native code is not an option, it has facilities for setting the alignment strategy used.

    The alignment strategy for members of a Java class that extendStructure can be set by invokingStructure.setAlignType(int alignType) method. There are four options for alignment type as described in the table below.

    Alignment SpecificationJNA Description
    ALIGN_DEFAULTUse the platform default alignment.
    ALIGN_GNUCvalidated for 32-bit x86 linux/gcc; align field size, max 4 bytes
    ALIGN_MSVCvalidated for w32/msvc; align on field size
    ALIGN_NONENo alignment, place all fields on nearest 1-byte boundary

    The output from dump() shown above makes it clear that the TWAIN native code uses no particular alignment strategy (ALIGN_NONE in the table above). But since this is not also JNA's default setting, all of the Java classes that substitute C structs have a default constructor that sets alignment type toALIGN_NONE (see The following code is an abbreviated view of the Java class for struct TW_IDENTITY with the default constructor.

    public class TW_IDENTITY extends Structure { public TW_IDENTITY() { setAlignType(Structure.ALIGN_NONE); } public int Id; public TW_VERSION Version = new TW_VERSION(); public short ProtocolMajor; public short ProtocolMinor; . . . }

    In general, there is no way of knowing the alignment strategy used by any particular native library. So, if a DLL's documentation does not specify this information some experimentation will be required to determine the correct alignment setting to use.

    Running the Sample Code

    To run the sample code described in this article proceed as follows:

    • Download the zip containing the sample code, and extract it into a directory (say, samples)
    • Open a command window, and use the "CD" command to navigate to the samples\code directory.
    • Execute the batch file build.bat. This compiles all of the code (and is required to be run just once). The class files are located in a directory called samples\bin.
    • To run a program execute the batch file with the same name (e.g. LockWorkStation.bat, BeepMorse.bat,GetLogicalDrives.bat, GetSystemTime.bat,GetVolumeInformation.bat, or JTwainDemo.bat)

    The samples zip containsjna.jar, so you don't have to download anything else. The batch files listed above also have the classpath specified, so you don't have to change anything to compile and run the sample code.