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.
To pass and receive structures from DLL methods, 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); // and so on. } }
Note Classes declared with @dll.struct are untrusted and therefore cannot be used by untrusted applets.
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. 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.)
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, 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 manually nesting structures (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.
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 arrays of scalars embedded in structures can be specified using the @dll.structmap directive. The following 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 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 @dll.struct directive is very similar to the @com.struct directive emitted by jactivex and implicitly emitted by javatlb. (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. As a result, 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 manually generate the classes.