J/Direct | CodeGuru

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 […]

Written By
CodeGuru Staff
CodeGuru Staff
Mar 1, 2001
8 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More

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


WindowsSystem 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

The


@dll.import

directive, your one and only way to J/Direct, is quite flexible. It has a


number of modifiers that you can use to customize the way you link to the


non-Java code. It can also be applied to some methods within a class or to a


whole class, meaning that all of the methods you declare in that class are


implemented in the same DLL. Let’s look at these features.


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

The


@dll.import
directive can be applied to an entire class, meaning that all of the methods in
that class are implemented in the same DLL and with the same linkage
attributes. The directive is not inherited by subclasses; for this reason, and
since functions in a DLL are by nature
static
functions, a better design approach is to encapsulate the API functions in a
separate class, as shown here:
/** @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.


Advertisement

Marshaling

Marshaling


means converting a function argument from its native binary representation into


some language-independent format, and then converting this generic


representation into a binary format that is appropriate to the called function.


In the example above, we called the


MessageBox( )

function and passed it a couple of


String

s.


MessageBox( )

is a C function, and the binary layout of Java


String

s


is not the same as C strings, but the arguments are nonetheless correctly


passed. That’s because J/Direct takes care of converting a Java


String

into a C string before calling the C code. This happens with all standard Java


types. Below is a table of the implicit conversions for simple data types:

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
*

The


list continues, but this gives you the idea. In most cases, you do not need to


worry about converting to and from simple data types, but things are different


when you must pass arguments of user-defined data types. For example, you might


need to pass the address of a structured, user-defined data type, or you might


need to pass a pointer to a raw memory area. For these situations, there are


special compiler directives to mark a Java class so that it can be passed as a


pointer to a structure (the

@dll.struct
directive). For details on the use of these keywords, please refer to the
product documentation.

Writing
callback functions

Some


Win32 API functions require a function pointer as one of the parameters. The


Windows API function may then call the argument function, possibly at a later


time when some event occurs. This technique is called a

callback
function
.
Examples include window procedures and the callbacks you set up during a print
operation (you give the print spooler the address of your callback function so
it can update the status and possibly interrupt printing).

Another


example is the


EnumWindows( )

API function that enumerates all top-level windows currently present in the


system.


EnumWindows( )

takes a function pointer, then traverses a list maintained internally by


Windows. For every window in the list, it calls the callback function, passing


the window handle as an argument to the callback.

To


do the same thing in Java, you must use the


Callback

class


in the


com.ms.dll

package. You inherit from


Callback

and


override


callback( )

.


This method will accept only


int

parameters and will return


int

or


void

.


The method signature and implementation depends on the Windows API function


that’s using this callback.

Now


all we need to do is create an instance of this


Callback

-derived


class and pass it as the function pointer argument to the API function.


J/Direct will take care of the rest.

The


example below calls the


EnumWindows( )

Win32 API; the


callback( )

method in the EnumWindowsProc class gets the window handle for each top-level


window, obtains the caption text, and prints it to the console window.

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.


Advertisement

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.

For


a more detailed discussion of these features, consult the Microsoft


documentation.


Contents

|

Prev

|

Next
CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.