This section explains how you can pass a String in ANSI or Unicode format to a DLL function. It also discusses two ways to return a String from a DLL function.
To pass a standard null-terminated string to a DLL function, pass a Java String.
For example, to change the current directory, access the Kernel32 function CopyFile function as follows:
class ShowCopyFile { public static void main(String args[]) { CopyFile("old.txt", "new.txt", true); } /** @dll.import("KERNEL32") */ private native static boolean CopyFile(String existingFile, String newFile, boolean f); }
Strings are read-only in Java; the Microsoft VM will only convert the String object as an input. To allow virtual machine implementations to marshal strings without copying the characters, string object parameters should not be passed to DLL functions that can modify the string. If the DLL function might modify the string, pass a StringBuffer object.
Strings are converted to ANSI unless the unicode or ole modifier is used, in which case the String is passed in Unicode format.
Strings cannot be declared as return types of a DLL function except in ole mode, where the native return type is assumed to be a LPWSTR allocated using the CoTaskMemAlloc function.
There are two common ways of passing a String back from a function: either the caller allocates a buffer that is filled in by the function, or the function allocates the String and returns it to the caller. Most Win32 functions use the first method, but OLE functions use the second method. (See Invoking OLE API Functions to learn about the special support that Microsoft® J/Direct provides for calling OLE functions.) One function that uses the first method is the Kernel32 function GetTempPath, which has the following prototype:
DWORD GetTempPath(DWORD sizeofbuffer, LPTSTR buffer);
This function simply returns the path of the system temporary file directory (such as C:\Tmp\). The buffer argument points to a caller-allocated buffer that receives the path, and sizeofbuffer indicates the number of characters that can be written to the buffer. (This is different from the number of bytes in the Unicode version.) In Java, Strings are read-only; you cannot pass a String object as the buffer. Instead, use Java's StringBuffer class to create a writable StringBuffer object. The following example shows invoking the GetTempPath function:
class ShowGetTempPath { static final int MAX_PATH = 260; public static void main(String args[]) { StringBuffer temppath = new StringBuffer(MAX_PATH); GetTempPath(temppath.capacity()+1, temppath); System.out.println("Temppath = " + temppath); } /** @dll.import("KERNEL32") */ private static native int GetTempPath(int sizeofbuffer, StringBuffer buffer); }
To understand this example, it is important to distinguish between a StringBuffer's length and its capacity. The length is the number of characters logically in the string currently stored in the StringBuffer. The capacity is the actual amount of storage currently allocated to that StringBuffer. After the following statement executes,
StringBuffer sb = new StringBuffer(259);
the value of sb.length is 0 (zero), and the value of sb.capacity is 259. When you invoke a DLL method passing a StringBuffer, the Microsoft VM examines the capacity of the StringBuffer, adds 1 for the null terminator, multiplies by 2 if Unicode is the default character size, and then allocates that many bytes of memory for the buffer that is passed to the DLL function. In other words, you use the capacity, not the length, to set the size of the buffer. Be careful not to make the following mistake:
StringBuffer sb = new StringBuffer(); //Wrong! GetTempPath(MAX_PATH, sb);
Invoking the StringBuffer constructor with no arguments creates a StringBuffer object with a capacity of 16, which is probably too small. MAX_PATH was passed to the GetTempPath method, indicating that there was enough room in the buffer to hold 260 characters. Thus, GetTempPath will probably overrun the buffer. If you were planning to use GetTempPath extensively, you should wrap it in the following manner:
public static String GetTempPath() { StringBuffer temppath = new StringBuffer(MAX_PATH-1); int res = GetTempPath(MAX_PATH, temppath); if (res == 0 || res > MAX_PATH) { throw new RuntimeException("GetTempPath error!"); } return temppath.toString(); // can't return a StringBuffer }
This method offers both convenience and safety, and it maps the error return value of the function to a Java exception. Notice that you cannot return StringBuffer objects.