JDirect
 In this topic

*Quick Reference

*Basic Scalar Types

*Chars

*Booleans

*Strings

*Arrays

*Structures

*Pointers

*Polymorphic Parameters

*Callbacks

 

JDirect    PreviousJDirectNext
About J/Direct     Previous JDirect Next

 


How Data Types are Marshaled

The Microsoft Win32 VM for Java infers the native type of each parameter and the return value from the declared (compile-time) Java type of the parameter. For example, a parameter declared as a Java integer is passed as a 32-bit integer; a parameter declared as a Java String object is passed as a null-terminated string, and so forth. There are no invisible attributes that provide information about the native types. In Java, what you see is what you get.

The following sections describe in detail how the Java data types map to native data types.

Quick Reference Provides tables that list the Java type that maps to each native type.
Basic Scalar Types Describes the mappings for int, byte, short, long, float, and double.
Chars Explains the mapping of the char data type.
Booleans Explains the mapping of the boolean data type.
Strings Explains how to pass and receive strings.
Arrays Explains how to pass arrays of scalars.
Structures Explains how to use the @dll.struct directive to pass and receive structures and how to use @dll.structmap to embed fixed-sized arrays within structures.
Pointers Explains how to handle DLLs that have multiple return values and how to read and write data through raw pointers.
Polymorphic Parameters Explains how to simulate polymorphic parameters.
Callbacks Explains how to declare and invoke a DLL function that takes a Callback parameter and how to embed a Callback inside a structure.

Quick Reference

The following two tables list the native type that corresponds to each Java type. The first table describes the mappings for parameters and return values, and the second table shows the mappings that are used with the @dll.struct directive.

Parameter and Return Value Mappings

Java Native Notes/Restrictions
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 Not allowed as return value, except in ole mode. In ole mode, String maps to LPWSTR. The Microsoft VM frees the string by using CoTaskMemFree.
StringBuffer LPTSTR Not allowed as return value. Set the StringBuffer capacity large enough to hold the largest string that the DLL function can generate.
byte[] BYTE*
short[] WORD*
char[] TCHAR*
int[] DWORD*
float[] float*
double[] double*
long[] __int64*
boolean[] BOOL[]
Object pointer to struct In ole mode, an IUnknown* is passed instead.
Interface COM interface Use jactivex or similar tool to generate interface file.
com.ms.com.SafeArray SAFEARRAY* Not allowed as return value.
com.ms.com._Guid GUID,IID,CLSID
com.ms.com.Variant VARIANT*
@dll.struct classes pointer to struct
@com.struct classes pointer to struct
void VOID As return value only.
com.ms.dll.Callback function pointer As parameter only.



Mappings Used with @dll.struct

Java Native
byte BYTE or CHAR
char TCHAR
short SHORT or WORD
int INT, UINT, LONG, ULONG or DWORD
long __int64
float float
double double
boolean BOOL[]
Java Native
String Pointer to a string, or an embedded fixed-size string
Class marked with @dll.struct Nested structure
char[] Nested array of TCHAR
byte[] Nested array of BYTE
short[] Nested array of SHORT
int[] Nested array of LONG
long[] Nested array of __int64
float[] Nested array of floats
double [] Nested array of doubles

Basic Scalar Types

The basic scalar types are mapped as you would expect.
Java type DLL
int signed 32-bit integer
byte signed 8-bit integer
short signed 16-bit integer
long signed 64-bit integer
float 32-bit float
double 64-bit double

There is no direct representation of unsigned integer types in Java, except by using the signed types as a two's complement representation. For example, the integer type can be used without loss of representation for the common DWORD (unsigned 32-bit) type.

Chars

The Java char type becomes a CHAR (an 8-bit ANSI character) unless the unicode or ole modifier is in effect, in which case it becomes a WCHAR (a 16-bit Unicode character).

Booleans

The Java boolean type maps to the Win32 BOOL type, which is a 32-bit type. As a parameter, the Java true maps to 1, and false maps to 0. As a return value, all non-zero values map to true.

Note that BOOL and VARIANT_BOOL (the internal boolean type in Microsoft® Visual Basic®) are not interchangeable. To pass a VARIANT_BOOL to a Visual Basic DLL, you must use the Java short type and use -1 for VARIANT_TRUE, and 0 for VARIANT_FALSE.

Strings

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.

Passing a String to a DLL Function

To pass a standard null-terminated string to a DLL function, just pass a Java String.

For example, to change the current directory, you can 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, so 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.

Receiving a String From a DLL 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 tempfile 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, so you cannot pass a String object as the buffer. Instead, you can use Java's StringBuffer class to create a writable StringBuffer object. Here is an example that invokes 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 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 one 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.

Arrays

J/Direct automatically handles arrays of scalars. The following Java array types translate directly into native pointer types as follows.
Java Native # of bytes per element
byte[] BYTE* 1
short[] SHORT* 2
int[] DWORD* 4
float[] FLOAT* 4
double[] DOUBLE* 8
long[] __int64* 8
boolean[] BOOL* 4

The char[] array type maps to CHAR* unless the unicode modifier is in effect, in which case it maps to WCHAR*.

All scalar array parameters can be modified by the caller (like [in,out] parameters).

Array types cannot be used as return types. There is no support for arrays of objects or strings.

Typically, this facility is used by OLE functions to return values. (OLE functions reserve the "function" return value to return the HRESULT error code.) See Invoking OLE API Functions to learn how to obtain the return value for OLE functions.

Structures

The Java language does not directly support the concept of a structure. Although Java classes containing fields can be used to emulate the concept of a structure within the Java language, ordinary Java objects cannot be used to simulate structures in native DLL calls. This is because the Java language does not guarantee the layout of the fields and because the garbage collector is free to move the object around in memory.

Therefore, to pass and receive structures from DLL methods, you need to use the @dll.struct compiler directive. When applied to a Java class definition, this directive causes all instances of the class to be allocated in a memory block that will not move during garbage collection. In addition, the layout of the fields in memory can be controlled using the pack modifier (see Structure Packing). For example, the Win32 SYSTEMTIME structure has the following definition in the C programming language.


typedef struct {
  WORD wYear;
  WORD wMonth;
  WORD wDayOfWeek;
  WORD wDay;
  WORD wHour;
  WORD wMinute;
  WORD wSecond;
  WORD wMilliseconds;
} SYSTEMTIME;

The correct declaration of this structure in Java is as follows.


/** @dll.struct() */
class SYSTEMTIME {
  public short wYear;
  public short wMonth;
  public short wDayOfWeek;
  public short wDay;
  public short wHour;
  public short wMinute;
  public short wSecond;
  public short wMilliseconds;
} 

The following example uses the SYSTEMTIME structure in a DLL method call.


class ShowStruct {
    /** @dll.import("KERNEL32") */
    static native void GetSystemTime(SYSTEMTIME pst);
    public static void main(String args[])
    {
      SYSTEMTIME systemtime = new SYSTEMTIME();
      GetSystemTime(systemtime);
      System.out.println("Year is " + systemtime.wYear);
      System.out.println("Month is " + systemtime.wMonth);
      // etc.
    }
} 

Note: Classes declared with @dll.struct are considered unsafe and therefore cannot be used by untrusted applets.

Correspondence Between Types Inside Structures

The following table describes how scalar types map inside structures.
Java Native
byte BYTE
char TCHAR (CHAR or WCHAR depending on @dll.struct definition)
short SHORT
int LONG
long __int64
float float
double double
boolean BOOL (32-bit boolean)

Reference types (Java objects and classes) normally map to embedded structures and arrays. Each supported mapping is described in the following table.
Java Native
String Pointer to a string, or an embedded fixed-size string
Class marked with @dll.struct Nested structure
char[] Nested array of TCHAR (CHAR/WCHAR)
byte[] Nested array of BYTE
short[] Nested array of SHORT
int[] Nested array of LONG
long[] Nested array of __int64
float[] Nested array of floats
double [] Nested array of doubles

There is no direct support for pointers inside structures, due to the large number of possible ways referenced objects could be allocated and disposed of. To represent a structure with an embedded pointer, declare the pointer field as type int. You will need to make explicit DLL calls to the appropriate allocation functions and initialize the memory blocks yourself. (You could use DllLib.ptrToStruct to map the blocks onto @dll.struct classes.)

Nested Structures

A structure can embed another structure simply by naming the other structure as the field type. For example, the Windows MSG structure embeds a POINT structure as follows.


	typedef struct {
		LONG x;
		LONG y;
	} POINT;

	typedef struct {
		int hwnd;
		int message;
		int wParam;
		int lParam;
		int time;
		POINT pt;
	} MSG;

This translates directly into Java as follows.


	/** @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;
	}

Performance tip: Although embedding structures is handy, the fact remains that Java does not truly support embedded objects – only embedded references to objects. The Microsoft VM must translate between these two formats each time a nested structure is passed. Therefore, in a critical code path, you can improve performance by nesting structures manually (by copying the fields of the nested structure into the containing structure). For example, the pt field in the MSG structure could easily be declared as two integer fields, pt_x and pt_y.

Fixed-size Strings Embedded within Structures

Some structures have fixed size strings embedded in them. The LOGFONT structure is defined as follows.


   typedef struct {
     LONG lfHeight;
     LONG lfWidth;
     /* <many other fields deleted for brevity> */
     TCHAR lfFaceName[32];
   } LOGFONT;

This structure can be expressed in Java using an extension syntax to specify the size.


   /** @dll.struct() */
   class LOGFONT {
     int lfHeight;
     int lfWidth;
     /* <many other fields deleted for brevity> */
     /** @dll.structmap([type=TCHAR[32]]) */
     String	lfFaceName;
   } 

The @dll.structmap directive indicates the size of the fixed string as measured in characters (including space for the null terminator).

Fixed-size Scalar Arrays Embedded within Structures

Fixed-size arrays of scalars embedded in structures can be specified using the @dll.structmap directive. Here is a C language structure that contains fixed-size scalar arrays.


struct EmbeddedArrays
{
  BYTE     b[4];
  CHAR     c[4];
  SHORT    s[4];
  INT      i[4];
  __int64  l[4];
  float    f[4];
  doubl    d[4];
};

You can specify the EmbeddedArrays structure by using @dll.structmap in the following way.


/** @dll.struct() */
class EmbeddedArrays
{
    /** @dll.structmap([type=FIXEDARRAY, size=4]) */
    byte b[];

    /** @dll.structmap([type=FIXEDARRAY, size=4]) */
    char c[];

    /** @dll.structmap([type=FIXEDARRAY, size=4]) */
    short s[];

    /** @dll.structmap([type=FIXEDARRAY, size=4]) */
    int i[];

    /** @dll.structmap([type=FIXEDARRAY, size=4]) */
    long l[];

    /** @dll.structmap([type=FIXEDARRAY, size=4]) */
    float f[];

    /** @dll.structmap([type=FIXEDARRAY, size=4]) */
    double d[];

}

Structure Packing

Structure fields are padded and aligned according to ANSI 3.5.2.1. The packing size can be set using the pack modifier.


   /** @dll.struct(pack=n) */

where n can be 1, 2, 4 or 8. The default is 8. For users of the Microsoft® Visual C++® compilers, "pack=n" is equivalent to "#pragma pack(n)".

The Relationship Between @dll.struct and @com.struct

The @dll.struct directive is very similar to the @com.struct directive emitted by the jactivex tool and emitted (implicitly) by the javatlb tool. (The javatlb tool has been replaced by jactivex.) The main difference is that the default type mappings are suited for Microsoft® Windows® function calling instead of COM object calling. Given this information, it follows that you can also generate @dll.struct classes by describing the structure in a type library and using jactivex to generate the Java class. However, it's usually faster to generate the classes manually.

Pointers

Java does not support a pointer data type. However, instead of passing a pointer, you can pass a one-element array. You can store pointers in Java integers, and you can read and write data from raw pointers.

Return Value Pointers

Win32 functions that have multiple return values typically handle them by having the caller pass a pointer to a variable to be updated. For example, the GetDiskFreeSpace function has the following prototype.


   BOOL GetDiskFreeSpace(LPCTSTR szRootPathName, 
                         DWORD  *lpSectorsPerCluster,
                         DWORD  *lpBytesPerCluster, 
                         DWORD  *lpFreeClusters,
                         DWORD  *lpClusters);

GetDiskFreeSpace is typically called as follows.


DWORD sectorsPerCluster, bytesPerCluster, freeClusters, clusters;
GetDiskFreeSpace(rootname, &sectorsPerCluster, 
                 &bytesPerCluster, &freeClusters, &clusters);

In Java, this is just a special case of passing scalar arrays where the array size is one element. The following example shows how to call the GetDiskFreeSpace function.


class ShowGetDiskFreeSpace 
{
  public static void main(String args[])
  {
    int sectorsPerCluster[] = {0};
    int bytesPerCluster[] = {0};
    int freeClusters[] = {0};
    int clusters[] = {0};
    GetDiskFreeSpace("c:\\", sectorsPerCluster, bytesPerCluster, 
                     freeClusters, clusters);
    System.out.println("sectors/cluster  = " + sectorsPerCluster[0]);
    System.out.println("bytes/cluster = " + bytesPerCluster[0]);
    System.out.println("free clusters = " + freeClusters[0]);
    System.out.println("clusters = " + clusters[0]);
  }

  /** @dll.import("KERNEL32") */
  private native static boolean GetDiskFreeSpace(String rootname,
                   int pSectorsPerCluster[], int pBytesPerCluster[], 
                   int pFreeClusters[], int pClusters[]);
}

Raw Pointers

A pointer to an unknown or particularly difficult structure can be stored in a plain Java integer. If your application only needs to store the pointer and not dereference it, this is the simplest (and most efficient) approach. You might want to use this technique to store a pointer that has been returned by a DLL function that allocates a memory block. In fact, you can use this technique to store a pointer returned by any DLL function. Needless to say, using raw pointers eliminates many of the safety advantages of Java. So, an alternative approach should be used whenever possible. However, there are situations when you might choose to use raw pointers. With that in mind, there are two ways to read and write data from raw pointers.

Casting to a Reference to an @dll.struct Class

One way to read and write data through a raw pointer is to cast the raw pointer to a reference to an @dll.struct class. Once this is done, you can read and write the data using normal field access syntax. For instance, suppose you have a raw pointer that you wish to access as a RECT. You can use the system method DllLib.ptrToStruct as follows.


  /** @dll.struct() */
  class RECT {
    int left;
    int top;
    int right;
    int bottom;
  }

  import com.ms.dll.*;

  int  rawptr = ...;
  RECT rect = (RECT)DllLib.ptrToStruct(RECT.class, rawptr);
  rect.left = 0;
  rect.top = 0;
  rect.right = 10;
  rect.bottom = 10;

The ptrToStruct method wraps the raw pointer in a RECT instance. Unlike instances created by the new operator, this RECT instance will not attempt to free the raw pointer upon reclamation by the garbage collector because the RECT object has no way of knowing how the pointer was allocated. In addition, because the native memory was already constructed at the time ptrToStruct was called, the RECT class constructor is not called.

Using the DllLib Copy Methods

Another method for reading and writing data through a raw pointer is to use the overloaded copy methods in DllLib. These methods copy data between Java arrays of various types and raw pointers. If you need to treat a raw pointer as a pointer to a string (LPTSTR), you can use one of the DllLib methods ptrToStringAnsi, ptrToStringUni, or ptrToString to parse the string and convert it into a java.lang.String object.

 
  import com.ms.dll.*;

  int rawptr = ...;
  String s = DllLib.ptrToStringAnsi(rawptr);

Warning: All Java objects are subject to movement in memory or reclamation by the garbage collector. Therefore, you should not attempt to obtain a pointer to a Java array by calling a DLL function that does generic casting. The following example shows you an incorrect way to obtain the pointer.


  // Do not do this!
  /** @dll.import("MYDLL") */
  private native static int Cast(int javaarray[]);

	
  // Inside MYDLL.DLL   
  LPVOID Cast(LPVOID ptr) 
  {
    // Do not do this!
    return ptr; // comes in as a Java array; goes out as a Java int
  } 

The value of ptr is guaranteed to be valid only for the duration of the call to the Cast function. This is because VM implementations are allowed to implement passing of arrays by copying rather than pinning and because garbage collection may cause the physical location of the array to be different after the call to the Cast function returns.

Polymorphic Parameters

Some Win32 functions declare a parameter whose type depends on the value of another parameter. For example, the WinHelp function is declared as follows.


  BOOL WinHelp(int hwnd, LPCTSTR szHelpFile, UINT cmd, DWORD dwData);

The innocent-looking dwData parameter can actually be any one of the following: a pointer to a string, a pointer to a MULTIKEYHELP structure, a pointer to a HELPWININFO, or a plain integer, depending on the value of the cmd parameter.

J/Direct offers two ways to declare such a parameter:

  • Declare the parameter to be type Object.
  • Use overloading to declare a separate method for each possible type.

Declaring the Parameter as Type Object

Here is how to declare WinHelp by declaring dwData as type Object.


  /** @dll.import("USER32") */
  static native boolean WinHelp(int hwnd, String szHelpFile, 
                                int cmd, Object dwData);

When WinHelp is invoked, J/Direct will use the runtime type to determine how to translate dwData. The following table describes how the types are translated.

Type Translated as
java.lang.Integer 4-byte integer
java.lang.Boolean 4-byte BOOL
java.lang.Char CHAR (or WCHAR if the unicode or ole modifiers are in effect)
java.lang.Short 2-byte SHORT
java.lang.Float 4-byte FLOAT
java.lang.String LPCSTR (or LPCWSTR if the unicode or ole modifier is in effect)
java.lang.StringBuffer LPSTR (or LPWSTR if the unicode or ole modifier is in effect)
byte[] BYTE*
char[] CHAR* (or WCHAR* if the unicode or ole modifier is in effect)
short[] SHORT*
int[] INT*
long[] __int64
float[] float*
double[] double*
@dll.struct pointer to structure

Overloading the Function

Another way to declare the WinHelp function is to overload the function for each possible type.


/** @dll.import("USER32") */
static native boolean WinHelp(int hwnd, String szHelpFile, 
                              int cmd, int dwData);
	
/** @dll.import("USER32") */
static native boolean WinHelp(int hwnd, String szHelpFile, 
                              int cmd, String dwData);	

/** @dll.import("USER32") */
static native boolean WinHelp(int hwnd, String szHelpFile, 
                              int cmd, MULTIKEYHELP dwData);	

/** @dll.import("USER32") */
static native boolean WinHelp(int hwnd, String szHelpFile, 
                              int cmd, HELPWININFO dwData);

You cannot handle a polymorphic return value using overloading because Java methods cannot be overloaded on return value only. Therefore, you need to give each variant of the function a different Java name and use the entrypoint modifier to link them all to the same DLL method. (See Aliasing (Method Renaming) to learn more about renaming DLL methods.)

Comparison Between the Two Methods

In most cases (including the WinHelp case), overloading is the preferred approach because it offers superior run time performance as well as better type checking. In addition, overloading avoids the need to wrap integer arguments inside an Integer object.

However, declaring the parameter as type Object can be useful in cases where there is more than one polymorphic parameter. You might also choose this method when you want to access a service that acts generically on a wide variety of types, such as a function that can accept any object declared with @dll.struct.

Callbacks

You can write callbacks in Java by extending the system class com.ms.dll.Callback.

Declaring a Method that Takes a Callback

To represent a callback parameter in Java, declare the Java type to be either type com.ms.dll.Callback or a class that derives from Callback. For example, the Microsoft® Win32® EnumWindows function is prototyped as follows.


  BOOL EnumWindows(WNDENUMPROC wndenumproc, LPARAM lparam);

The corresponding Java prototype is shown in the following example.


  import com.ms.dll.Callback;
  /** @dll.import("USER32") */
  static native boolean EnumWindows(Callback wndenumproc, 
                                    int lparam);

Invoking a Function that Takes a Callback

To invoke a function that takes a callback, you need to define a class that extends Callback. The derived class must expose one non-static method whose name is callback (all lowercase). Continuing with the EnumWindows previous example, the C language definition of WNDENUMPROC looks like this.


  BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lparam);

To author an EnumWindowsProc in Java, you declare a class that extends Callback as follows.


  class EnumWindowsProc extends Callback
  {
    public boolean callback(int hwnd, int lparam)
    {
      StringBuffer text = new StringBuffer(50);
      GetWindowText(hwnd, text, text.capacity()+1);
			
      if (text.length() != 0) {
        System.out.println("hwnd = " + Integer.toHexString(hwnd) +
                           "h: Text = " + text);
      } 
      return true;  // return TRUE to continue enumeration.
    }
  
  /** @dll.import("USER32") */
  private static native int GetWindowText(int hwnd, StringBuffer text, 
                                          int cch);
  }

You can invoke EnumWindows with this Callback in the following way.


  boolean result = EnumWindows(new EnumWindowsProc(), 0);

Restrictions on Types Accepted by the Callback Method

The return type of the callback method must be void, int, boolean, char, or short. The only parameter type currently allowed is the int type. Fortunately, this is not as restrictive as it sounds. You can use the DllLib methods ptrToStringAnsi, ptrToStringUni, and ptrToString to treat a parameter as an LPTSTR. You can use the ptrToStruct method to treat a parameter as a pointer to an @dll.struct class.

Associating Data with a Callback

Frequently, it is necessary to pass some data from the caller of the function to the callback. This explains why EnumWindows takes an extra lparam argument. Most Win32 functions that take callbacks accept one extra 32-bit parameter that is passed to the callback without interpretation. With the Callback mechanism, it is not necessary to pass data using the lparam argument. Because the callback method is non-static, you can store your data as fields in the EnumWindowsProc object.

The Lifetime of a Callback

Some care is required to ensure that the Callback is not reclaimed by garbage collection before the native function is finished with it. If the callback is short-term (only callable for the duration of one function call), no special action is required because a Callback passed to a DLL function is guaranteed not to be reclaimed by garbage collection while the call is in progress.

If a callback is long-term (used across function calls), you will need to protect the Callback from being reclaimed, typically by storing a reference to it in a Java data structure. You can also store references to Callbacks within native data structures by using the com.ms.dll.Root class to wrap the Callback inside a root handle. The root handle is a 32-bit handle that prevents the Callback from being reclaimed until the handle is explicitly freed. For example, a root handle to a WndProc can be stored in the application data area of an HWND structure, and then explicitly freed on the WM_NCDESTROY message.

Embedding a Callback Inside a Structure

To embed a callback inside a structure, you can first call the com.ms.dll.Root.alloc method to wrap the Callback in a root handle. Then pass the root handle to the DllLib.addrOf method to obtain the actual (native) address of the callback. Then, store this address as an integer.

For example, the WNDCLASS structure can be declared in Java as follows.


  /** @dll.struct() */
  class WNDCLASS {
    int style;
    int	lpfnWndProc; // CALLBACK
    ... /* <other fields deleted for brevity> */
  } 

Let's assume you have extended Callback as follows.


  class WNDPROC extends Callback
  {
    public int callback(int hwnd, int msg, int wparam, int lparam)
    {	
	...
    }
  } 

To store a pointer to the callback inside the WNDCLASS object, use the following sequence.


  import com.ms.dll.*;

  WNDCLASS wc = new WNDCLASS();
  int callbackroot = Root.alloc(new WNDPROC());
  wc.lpfnWndProc = DllLib.addrOf(callbackroot);

Top © 1998 Microsoft Corporation. All rights reserved. Terms of use.