This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.


MIND


This article assumes you're familiar with Java and Win32
Download the code (2KB)

Using J/Direct to Call the Win32 API from Java
Mike Pietraszak

Many programmers have taken a shine to Java, but have been frustrated when trying to take full advantage of the Win32 API. J/Direct solves this problem, letting you use Java to full advantage with Windows.
While the pundits and purists debate the merits of lowest-common-denominator programming, many developers back on planet Earth are busy writing Windows®-based apps. And with the advent of J/Direct, you can enjoy the liberating productivity benefits of the Java language without being weighed down by libraries that cater to even the least functional of the "pure" platforms. Now developers who choose to throw off the shackles of purity have a choice—and that choice includes the best of both the multiplatform and Windows-specific worlds.
      J/Direct is a new feature of the Microsoft® Virtual Machine (VM) that allows developers to call the entire Win32® API directly. Before J/Direct, developers who wanted to access the rich functionality of Win32 had two options. They could either wrap the Win32 API calls in a custom DLL and call the wrapper using the Raw Native Interface (RNI), or use the COM features of the VM to access the subset of Win32 APIs exposed through COM interfaces. Now J/Direct provides a third, more direct approach to accessing DLL-based Win32 APIs in a way that automatically converts native pointers, structures, and types to their Java equivalents.
      The simple program in Figure 1 demonstrates the J/Direct syntax. The program plays a WAV-format audio file (a format unsupported by the AWT applet.AudioClip class) using the sndPlaySound API found in the Windows Multimedia Library (WINMM.DLL). When the code is compiled by the Microsoft Compiler for Java (JVC.EXE) version 1.02.4213 or greater, the @dll.import directive is translated into special bytecode attributes in the HelloWindows class file. The VM for Java then decodes those attributes into an instruction to load the WINMM.DLL library. The sndPlaySound function can then be called like any other Java function in the HelloWindows class. So before you get started, you'll need the VM and the Microsoft Compiler for Java, both of which are included in the Microsoft SDK for Java 2.0. The SDK can be downloaded from http://www.microsoft.com/java.

The Best of Both Worlds
      The HelloWindows program in Figure 1 will run great, provided that the program uses the Microsoft VM version 4.79.2252 or greater that comes with Microsoft Internet Explorer 4.0 or the SDK 2.0. But in a Web-based world, developers may not have control over the VM their clients will be using to run Java programs. So it's possible that the program will be run on a VM that doesn't handle J/Direct calls appropriately. Developers can still use J/Direct in a way that permits Java-based programs to run successfully in both Windows-specific and heterogeneous client environments.
      Figure 2 is a modified listing for the HelloWindows program in Figure 1. HelloWindowsEx performs several checks to determine the scenario under which it is being run: the environment is Windows and J/Direct is available; the environment is Windows, but the VM-specific class com.ms.util.SystemVersionManager can't be loaded (so the VM probably isn't the Microsoft VM); or the environment is Windows and the virtual machine is the Microsoft VM, but it's not the correct version so there's J/Direct support. By writing code that recognizes these gradations, you can take advantage of Windows-specific features and still deploy to platforms that do not provide J/Direct access to Win32 APIs.
      So now you can use J/Direct to access APIs (and you can even rename them). How do you map the signature of the API you want to call (usually documented in C) to one that uses Java intrinsic types? Another good question. When J/Direct accesses a Windows DLL from Java, the Java parameters must be marshaled from Java types to native C types. For return values, the C types must be marshaled back to Java types. This conversion is handled automatically by the VM, according to the table shown in Figure 3. Note that some conversions are only done for parameters, like Strings and SafeArrays, and some are only done for return values, like void. But as for the actual @dll.import declaration in your code, you'll have to do that C to Java (or Visual Basic® to Java) conversion yourself.
      The C signature for the sndPlaySound function is:


 BOOL sndPlaySound(
   LPCSTR lpszSound,
   UINT fuSound
 );
So the parameter and return value translation to Java types is as follows:
C Type

Java Type
____________________________________________
BOOL
becomes      
boolean
LPCSTR
becomes
String
UNIT
becomes
int

The Java declaration for the API import statement then becomes:

 /** @dll.import("winmm") */
 static native boolean sndPlaySound(String lpszSound, int fuSound);
Converting Basic to Java
      If you are using a reference that lists APIs with Visual Basic types, they can also be converted easily to Java. Figure 4 shows a table with the necessary type mappings. The sndPlaySound API would be declared in Visual Basic as:

 Declare Function sndPlaySound Lib "winmm.dll" Alias "sndPlaySound" (ByVal
 lpszSoundName As String, ByVal uFlags As Long) As Long
The parameter and return value translation to Java types from Visual Basic is as follows:
Visual Basic Type

Java Type
____________________________________________
Long (return value)
becomes      
int
String
becomes
String
Long
becomes
int

      The return value for the C API reference was of type BOOL, and was converted to Java type boolean. For the Visual Basic API reference, the return value was type Long, which can be converted to either the Java type boolean or the Java type int. In this case, either type will work just fine. So the Java declaration derived from an API reference using Visual Basic types could be correctly translated as either


 /** @dll.import("winmm") */
 static native boolean sndPlaySound(String lpszSound, int fuSound);
or

 /** @dll.import("winmm") */
 static native int sndPlaySound(String lpszSound, int fuSound);
      J/Direct also provides a flexible way for developers to map a somewhat cryptic Win32 API to a more Java-friendly name. The sndPlaySound function, for example, can be mapped (or "aliased") to playWAV by modifying the following lines of code in the HelloWindows program:

 /** @dll.import("winmm") */
  static native boolean sndPlaySound (String lpszSound, int fuSound);
 
 /** @dll.import("winmm", entrypoint="sndPlaySound") */
  static native boolean playWAV (String lpszSound, int fuSound);
      Because J/Direct calls native APIs, the code must be authenticated and approved with maximum trust in order to be run from a browser. For J/Direct code to be run from an applet, it must be digitally signed, indicating full trust and granting all permissions. Untrusted applet code cannot access trusted applet classes that use J/Direct. Even after the applet has been signed, the init, start, stay, and destroy methods of the applet must include a call to com.ms.security.PolicyEngine.assertPermission. Tools like CABARC.EXE and SIGNCODE.EXE in the Microsoft SDK for Java 2.0 provide a means for packaging and digitally signing applets with the high levels of trust needed to be run from the browser.

Applets with J/Direct
      For security reasons, applets that make J/Direct calls must be packaged into a CAB and authenticated. Figure 5 shows an applet that makes J/Direct calls, and the following HTML hosts the applet:


 <html>
   <applet code="HelloIE" width=300 height=50>
     <param name="cabbase" value="hello.cab">
   </applet>
 </html>
Figure 6 is a .bat file that can be used to create the test certificate, shown in Figure 7, that will be displayed the first time the applet is loaded. For information on how to apply for an official certificate for commercial applications, visit http://msdn.microsoft.com/workshop/prog/security/authcode/codesign-f.htm.
Figure 7: Test Certificate
      Figure 7: Test Certificate

      You may find the edit-compile-debug cycle for J/Direct applets tricky because applet classes remain cached while Internet Explorer is running. This means that even if you recompile a class and redeploy it in a CAB, the old class will still be in the cache, so it will be run again and your changes will not be displayed. You can use Ctrl-F5 to force a reload of your classes, or you can restart Internet Explorer. Next, you must be sure to sign your code with low trust (signcode.exe -jp low) and request security permissions (with PolicyEngine.assertPermission). In this example, maximum permissions (SYSTEM) were requested. Finally, you'll have to turn on test certificates by running the SETREG.EXE tool.

What about COM and RNI?
      J/Direct is a new way for users of the Microsoft VM for Java to take advantage of Win32 APIs in Windows 95, Windows NT®, and beyond. But what's so new about Windows access? After all, VM users can already access COM through a host of tools like the Visual J++ Type Library Wizard for Java (JAVATLB.EXE), the Microsoft ActiveX Control Importer for Java (JACTIVEX.EXE), the Visual J++ ActiveX Wizard for Java, and the Java/COM Registration Utility (JAVAREG.EXE).
      But with J/Direct, the Windows APIs are accessed with DLL calls, not via COM. And although RNI provides a great way to access DLLs, the RNI technology assumes that you started with a Java class, generated a C header file using msjavah.exe, and then added functionality to the C DLL. But Win32 DLLs aren't callable by RNI in this way. Because Win32 API names don't conform to RNI naming conventions, and because RNI expects data types (like strings) to be Java-format types (the Win32 API uses C-format types), RNI cannot be used to access Win32 or third-party DLLs. In other words, a single API cannot follow both the RNI conventions and the Windows conventions required by existing Windows-based apps.
      J/Direct makes it easy to call DLL functions by automatically marshaling parameters from Java to native C types. Crossing the Java to C boundary also means dealing with garbage collection issues. In C, memory must be explicitly allocated and deallocated in code. But in Java, memory allocation and deallocation is done automatically by the VM. This difference can be problematic if a Java-allocated variable gets deallocated by the VM while still in use on the C side. J/Direct helps when crossing this boundary by not garbage collecting (deallocating) during an API call, but there are limitations. If you access a DLL via J/Direct, that DLL cannot access another DLL that is accessed via RNI. And DLL functions cannot modify standard Java objects directly—structures built using the @dll.struct directive must be used.
      In my next article, I'll cover the @dll.struct directive, OLE calling conventions, ANSI versus Unicode issues, callbacks, and the com.ms.dll package.

From the January 1998 issue of Microsoft Interactive Developer.