J/Direct

Bruce Eckel's Thinking in Java Contents | Prev | Next

J/Direct is the simplest way to call functions in a Win32 DLL. It was designed primarily to interface with the Win32 API, but you can use it to call any other APIs. The ease of use of this feature is counterbalanced by some limitations and reduced performance (compared to RNI). But J/Direct has distinct advantages. First, there is no need to write additional non-Java code, except the code in the DLL you want to call. In other words, you do not need a wrapper or proxy/stub DLL. Second, function arguments are automatically converted to and from standard data types. (If you must pass user-defined data types, J/Direct might not be the way to go.) Third, it’s simple and straightforward, as the example below shows. In just a few lines, this example calls the Win32 API function MessageBox( ), which pops up a little modal window with a title, a message, an optional icon, and a few buttons.

public class ShowMsgBox {
  public static void main(String args[]) 
  throws UnsatisfiedLinkError   {
    MessageBox(0,
      "Created by the MessageBox() Win32 func",
      "Thinking in Java", 0);
  }
  /** @dll.import("USER32") */
  private static native int 
  MessageBox(int hwndOwner, String text,
    String title, int fuStyle);
}

Amazingly, this code is all you need to call a function in a Win32 DLL using J/Direct. The key is the @dll.import directive before the MessageBox( ) declaration, at the bottom of the example code. It looks like a comment, but it’s not: it tells the compiler that the function below the directive is implemented in the USER32 DLL, and should be called accordingly. All you must do is supply a prototype that matches the function implementation in the DLL and call the function. But instead of typing in the Java version of each Win32 API function that you need, a Microsoft Java package does this for you (I’ll describe this shortly). For this example to work, the function must be exported by name by the DLL, but the @dll.import directive can be used to link by ordinal as well, i.e., you can specify the entry position of the function in the DLL. I’ll cover the features of the @dll.import directive later.

An important issue in the process of linking with non-Java code is the automatic marshaling of the function parameters. As you can see, the Java declaration of MessageBox( ) takes two String arguments, but the original C implementation takes two char pointers. The compiler automatically converts the standard data types for you, following the rules described in a later section.

Finally, you might have noticed the UnsatisfiedLinkError exception in the declaration of main( ). This exception occurs when the linker is unable to resolve the symbol for the non-Java function at run-time. This happens for a number of reasons: the .dll file was not found, it is not a valid DLL, or J/Direct is not supported by your virtual machine. For the DLL to be found, it must be in the Windows or Windows\System directory, in one of the directories listed in your PATH environment variable, or in the directory where the .class file is located. J/Direct is supported in the Microsoft Java compiler version 1.02.4213 or above, and in the Microsoft JVM version 4.79.2164 or above. To get the compiler version number, run JVC from the command line with no parameters. To get the JVM version number, locate the icon for msjava.dll, and using the context menu look at its properties.

The @dll.import directive

Aliasing and linking by ordinal
For the @dll.import directive to work as shown above, the function in the DLL must be exported by name. However, you might want to use a different name than the original one in the DLL (aliasing), or the function might be exported by number (i.e. by ordinal) instead of by name. The example below declares FinestraDiMessaggio( ) (the Italian equivalent of “MessageBox”) as an alias to MessageBox( ). As you can see, the syntax is pretty simple.

public class Aliasing {
  public static void main(String args[]) 
  throws UnsatisfiedLinkError   {
    FinestraDiMessaggio(0,
      "Created by the MessageBox() Win32 func",
      "Thinking in Java", 0);
  }
  /** @dll.import("USER32", 
  entrypoint="MessageBox") */
  private static native int 
  FinestraDiMessaggio(int hwndOwner, String text,
    String title, int fuStyle);
}

The next example shows how to link to a function in a DLL that is not exported by name, but by its position inside of the DLL. The example assumes that there is a DLL named MYMATH somewhere along your path, and that this DLL contains at position 3 a function that takes two integers and gives you back the sum.

public class ByOrdinal {
  public static void main(String args[]) 
  throws UnsatisfiedLinkError {
    int j=3, k=9;
    System.out.println("Result of DLL function:"
      + Add(j,k));
  }
  /** @dll.import("MYMATH", entrypoint = "#3") */
  private static native int Add(int op1,int op2);
}

You can see the only difference is the form of the entrypoint argument.

Applying @dll.import to the entire class
/** @dll.import("USER32") */
class MyUser32Access {
  public static native int 
  MessageBox(int hwndOwner, String text,
    String title, int fuStyle);
  public native static boolean 
  MessageBeep(int uType);
}
 
public class WholeClass {
  public static void main(String args[]) 
  throws UnsatisfiedLinkError {
    MyUser32Access.MessageBeep(4);
    MyUser32Access.MessageBox(0,
      "Created by the MessageBox() Win32 func",
      "Thinking in Java", 0);
  }
}

Since the MessageBeep( ) and MessageBox( ) functions are now declared as static in a different class, you must call them specifying their scope. You might think that you must use the approach above to map all of the Win32 API (functions, constants, and data types) to Java classes. Fortunately, you don’t have to.

The com.ms.win32 package

The Win32 API is fairly big – on the order of a thousand functions, constants, and data types. Of course, you do not want to write the Java equivalent of every single Win32 API function. Microsoft took care of this, distributing a Java package that maps the Win32 API to Java classes using J/Direct. This package, named com.ms.win32, is installed in your classpath during the installation of the Java SDK 2.0 if you select it in the setup options. The package is made up of large number of Java classes that reproduce the constants, data structures, and functions of the Win32 API. The three richest classes are User32.class, Kernel32.class, and Gdi32.class. These contain the core of the Win32 API. To use them, just import them in your Java code. The ShowMsgBox example above can be rewritten using com.ms.win32 as follows (I also took care of the UnsatisfiedLinkError in a more civilized way):

import com.ms.win32.*;
 
public class UseWin32Package {
  public static void main(String args[]) {
    try {
      User32.MessageBeep(
        winm.MB_ICONEXCLAMATION);
      User32.MessageBox(0,
        "Created by the MessageBox() Win32 func",
        "Thinking in Java",
        winm.MB_OKCANCEL |
        winm.MB_ICONEXCLAMATION);
    } catch(UnsatisfiedLinkError e) {
      System.out.println("Can’t link Win32 API");
      System.out.println(e);
    }
  }
}

The package is imported in the first line. The MessageBeep( ) and MessageBox( ) functions can now be called with no other declarations. In MessageBeep( ) you can see that importing the package has also declared the Win32 constants. These constants are defined in a number of Java interfaces, all named winx (x is the first letter of the constant you want to use).

At the time of this writing, the classes in the com.ms.win32 package are still under development, but usable nonetheless.

Marshaling

Java

C

byte

BYTE or CHAR

short

SHORT or WORD

int

INT, UINT, LONG, ULONG, or DWORD

char

TCHAR

long

__int64

float

Float

double

Double

boolean

BOOL

String

LPCTSTR (Allowed as return value only in ole mode)

byte[]

BYTE *

short[]

WORD *

char[]

TCHAR *

int[]

DWORD *

Writing callback functions

import com.ms.dll.*;
import com.ms.win32.*;
 
class EnumWindowsProc extends Callback {
  public boolean callback(int hwnd, int lparam) {
    StringBuffer text = new StringBuffer(50);
    User32.GetWindowText(
      hwnd, text, text.capacity()+1);
    if(text.length() != 0)
      System.out.println(text);
    return true;  // to continue enumeration.
  }
}
 
public class ShowCallback {
  public static void main(String args[])
  throws InterruptedException {
    boolean ok = User32.EnumWindows(
      new EnumWindowsProc(), 0);
    if(!ok)
      System.err.println("EnumWindows failed.");
    Thread.currentThread().sleep(3000);
  }
}

The call to sleep( ) allows the windows procedure to complete before main( ) exits.

Other J/Direct features

There are two more J/Direct features you can get using modifiers in the @dll.import directive. The first is simplified access to OLE functions, and the second is the selection of the ANSI versus Unicode version of API functions. Here is a short description of the two.

By convention, all OLE functions return a value of type HRESULT, which is a structured integer value defined by COM. If you program at the COM level and you want something different returned from an OLE function, you must pass it a pointer to a memory area that the function will fill with data. But in Java we don’t have pointers; also, this style is not exactly elegant. With J/Direct, you can easily call OLE functions using the ole modifier in the @dll.import directive. A native method marked as an ole function is automatically translated from a Java-style method signature, which is where you decide the return type, into a COM-style function.

The second feature selects between ANSI and Unicode string handling. Most Win32 API functions that handle strings come in two versions. For example, if you look at the symbols exported by the USER32 DLL, you will not find a MessageBox( ) function, but instead MessageBoxA( ) and MessageBoxW( ) functions, which are the ANSI and Unicode version, respectively. If you do not specify which version you want to call in the @dll.import directive, the JVM will try to figure it out. But this operation takes some time during program execution time that you can save with the ansi, unicode, or auto modifiers.



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: August 20, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT When you look at natural user interfaces as a developer, it isn't just fun and games. There are some very serious, real-world usage models of how things can help make the world a better place – things like Intel® RealSense™ technology. Check out this upcoming eSeminar and join the panel of experts, both from inside and outside of Intel, as they discuss how natural user interfaces will likely be getting adopted in a wide variety …

  • Live Event Date: July 30, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT You may already know about some of the benefits of Bluemix, IBM's open platform for developing and deploying mobile and web applications. Check out this upcoming eSeminar that focuses on building an Android application using the MobileData service, with a walk-through of the real process and workflow used to build and link the MobileData service within your application. Join IBM's subject matter experts as they show you the way to build a base …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds