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


java911@microsoft.com         Download the code (13KB)
Jonathan Locke

Parlez-vous J/Direct?

My February column covered the differences between the various Java native interfaces: J/Direct™, COM, RNI, and JNI. This month I'll delve more deeply into J/Direct to address a few of the questions that weren't answered in February. Keep in mind that most of the J/Direct declarations demonstrated here are conveniently available by simply importing classes from the package com.ms.win32. In fact, com.ms.win32 serves as a good point of reference if you're having trouble.


How do I call a function that returns a DWORD value through a pointer (for example, GetDiskFreeSpace)?

The kernel32.dll GetDiskFreeSpace function has this prototype:


 WINBASEAPI BOOL WINAPI GetDiskFreeSpace(LPCTSTR lpRootPathName,
                                         LPDWORD lpSectorsPerCluster, 
                                         LPDWORD lpBytesPerSector,
                                         LPDWORD lpNumberOfFreeClusters,
                                         LPDWORD lpTotalNumberOfClusters)
GetDiskFreeSpace takes the path to the root of a drive and the addresses of several DWORD variables. If the function returns TRUE, the DWORD variables have been modified to contain appropriate values.
      In J/Direct, a DWORD pointer is specified as an int array of length 1, as shown in Figure 1. Each int array is allocated with the new operator and then passed to GetDiskFreeSpace to be filled in. Notice that the @dll.import tag includes the auto modifier. On Windows NT®, which utilizes Unicode, the auto modifier will cause the VM to call GetDiskFreeSpaceW. On Windows® 95, which uses ANSI strings, the same modifier results in a call to GetDiskFreeSpaceA. In each case, the first parameter (lpRootPathName) will be translated from a Java string to either Unicode or ANSI, as appropriate.


How do I provide a character buffer for a DLL function that returns a string by reference (for example, GetEnvironmentVariable)?

The GetEnvironmentVariable function, found in Kernel32.dll, has the following prototype:


 WINBASEAPI DWORD WINAPI GetEnvironmentVariable(LPCTSTR lpName,
                                                LPTSTR lpBuffer, 
                                                DWORD nSize)
The function takes the name of an environment variable like PATH and returns the current value of the variable in the lpBuffer character buffer. The nSize parameter specifies the size of the buffer in characters.
      The GetEnv example, shown in Figure 2, uses J/Direct to call the GetEnvironmentVariable function. The declaration @dll.import("KERNEL32",auto) specifies that the function can be found in kernel32.dll and that the ANSI and Unicode versions of the function should be used automatically, as appropriate. The second parameter (lpBuffer) is declared as a StringBuffer. While this may seem slightly odd at first—a simple character array might seem like a more natural mechanism—it is correct.

Java Tip of the Month
      If you're having trouble getting J/Direct code to work, double-check your @dll tags. Make sure you've linked to the right DLLs and that you're using the auto keyword (if necessary). If you run out of ideas, you may want to surf over to the J/Direct troubleshooting page:
http://www.microsoft.com/java/sdk/default.htm

      The getEnv method calls GetEnvironmentVariable to fill in the StringBuffer value with the value of the environment variable passed in by name. If the return value is 0, the environment variable doesn't exist and null is returned. If the return value is larger than the capacity (not the length) of the StringBuffer, the buffer is resized to the required capacity and the call is retried. Finally, the StringBuffer's value (its content) is returned as a String.
      It is interesting to note that the auto keyword in the J/Direct import statement affects not only the lpName parameter when the function is called, but also the contents of the lpBuffer return value when the function returns. On Windows NT, the value is already expressed in Unicode, so no translation occurs. On Windows 95, the lpBuffer StringBuffer is translated from ANSI to Unicode automatically.

How do I pass a structure to a DLL function when I'm using J/Direct?

To declare a structure in J/Direct, you place a @dll.struct tag before a class declaration. The data members of the class are then mapped, in the order they occur, onto the structure. As shown in Figure 3, a POINT structure declared in this way is passed transparently to the GetCursorPos function, which takes a Win32® POINT structure. Since both structures contain 32-bit integers, the translation between Java Object and native structure is quite simple. But as you'll see in my answer to the next question, the VM's mapping job is not always so trivial.
      The @dll.struct tag is necessary for two reasons. First, it directs the VM to prevent the garbage collector from moving instances of the given class while in a J/Direct-native call. Second, it guarantees a precise layout of data members (which is important to native code). In fact, the @dll.struct tag has a special modifer, pack, that allows you to set structure alignment (also known as structure packing) to 1, 2, 4, or 8-byte boundaries. The default, 8, is generally correct in Win32.

Can a J/Direct structure contain a nested structure or a fixed-length buffer?

Yes. Nested structures are handled transparently by the VM. All you have to do is declare the nested member at the right point and the VM will insert it there. Both the containing structure and the contained structure have to be marked with the @dll.struct tag.

     /** @dll.struct() */    
     class POINT 
     {       
         int x;
         int y;
     }
 
     /** @dll.struct() */
     class MSG 
     {       
         public int hwnd;
         public int message;
         public int wParam;
         public int lParam;
         public int time;
         public POINT pt;    
     }
      The @dll.structmap tag can be used to specify fixed-length arrays and character buffers. For example, consider the kernel32.dll GetVersionEx function. It takes only an OSVERSIONINFO structure as an argument. OSVERSIONINFO looks roughly like this:

     struct OSVERSIONINFO
     {
         DWORD dwOSVersionInfoSize;
         DWORD dwMajorVersion;
         DWORD dwMinorVersion;
         DWORD dwBuildNumber;
         DWORD dwPlatformId;
         TCHAR szCSDVersion[128];
     };
The corresponding J/Direct declaration of this structure is shown in Figure 4. Notice how the @dll.structmap tag specifies that the csdVersion string is a 128-character, fixed-length string where the TCHAR type specifier is defined as a 16-bit Unicode character on Windows NT and as an 8-bit ANSI character on Windows 95.

How do I implement a callback function in J/Direct?


J/Direct callback functions are implemented by writing a class containing a callback method that extends com.ms.dll.Callback. The callback method must be named callback. Figure 5 implements a callback for EnumWindows, which enumerates all top-level windows on the system. The callback itself simply prints the window handle and window text for each window.

Is it possible to use J/Direct to write a standard Windows-based application?

Yes! The WinJava application I wrote (see Figure 6) registers a window class, creates a top-level window, and then drops into a good old Win32 message dispatch loop—all thanks to J/Direct.
      The WinJava sample application ties together all of the J/Direct features I've discussed in this column. It also demonstrates two additional features. The first is the use of @dll.import on an entire class. In the main WinJava class, I declare a tag marking the class as @dll.import("USER32",auto). This is simply shorthand for "import all static native methods in this class from user32.dll." This saves quite a bit of typing.
      The second new concept is brought into play when the WinJava window class is registered. In Figure 5, my callback was only in use for the duration of the native call. Since a window class continues to use the WNDPROC callback long after the class has been registered, you need to make sure that the WNDPROC doesn't get moved or reclaimed by the garbage collector in the meantime. I do this by allocating a root handle to the WNDPROC object (in this case, WinJavaWndProc) using com.ms.dll.Root.alloc, and then specifying the address of that object (determined by com.ms.dll.DllLib.addrOf) as the lpfnWndProc member of the WNDCLASSEX structure.


From the June 1998 issue of Microsoft Interactive Developer.