0
# JNA Structures & Type Mapping
1
2
This document covers JNA's structure mapping system, unions, custom type conversion, and callback functionality for seamless Java-to-native type interoperability.
3
4
## Core Imports
5
6
```java { .api }
7
import com.sun.jna.Structure;
8
import com.sun.jna.Union;
9
import com.sun.jna.IntegerType;
10
import com.sun.jna.NativeLong;
11
import com.sun.jna.NativeMapped;
12
import com.sun.jna.TypeMapper;
13
import com.sun.jna.Callback;
14
```
15
16
## Structure Class - Java to C Struct Mapping
17
18
The `Structure` class provides automatic mapping between Java classes and C structures:
19
20
```java { .api }
21
/**
22
* Base class for mapping Java objects to native structures
23
*/
24
public abstract class Structure {
25
/**
26
* Default constructor - allocates new native memory
27
*/
28
public Structure();
29
30
/**
31
* Constructor using existing memory
32
* @param p Pointer to existing structure memory
33
*/
34
public Structure(Pointer p);
35
36
/**
37
* Read structure fields from native memory
38
*/
39
public void read();
40
41
/**
42
* Write structure fields to native memory
43
*/
44
public void write();
45
46
/**
47
* Get size of structure in bytes
48
* @return Structure size including padding
49
*/
50
public int size();
51
52
/**
53
* Get pointer to structure memory
54
* @return Pointer to native structure
55
*/
56
public Pointer getPointer();
57
58
/**
59
* Read specific field from native memory
60
* @param fieldName Name of field to read
61
*/
62
public void readField(String fieldName);
63
64
/**
65
* Write specific field to native memory
66
* @param fieldName Name of field to write
67
*/
68
public void writeField(String fieldName);
69
70
/**
71
* Enable automatic read after native calls
72
* @param auto true to enable auto-read
73
*/
74
public void setAutoRead(boolean auto);
75
76
/**
77
* Enable automatic write before native calls
78
* @param auto true to enable auto-write
79
*/
80
public void setAutoWrite(boolean auto);
81
82
/**
83
* Get field order for structure layout
84
* Must be implemented by subclasses
85
* @return List of field names in memory order
86
*/
87
protected abstract List<String> getFieldOrder();
88
89
// Static utility methods
90
91
/**
92
* Create new structure instance
93
* @param type Structure class
94
* @return New instance of structure
95
*/
96
public static Structure newInstance(Class<?> type);
97
98
/**
99
* Create structure instance at pointer
100
* @param type Structure class
101
* @param p Pointer to existing memory
102
* @return Structure instance using existing memory
103
*/
104
public static Structure newInstance(Class<?> type, Pointer p);
105
106
/**
107
* Auto-read array of structures
108
* @param structures Array of structures to read
109
*/
110
public static void autoRead(Structure[] structures);
111
112
/**
113
* Auto-write array of structures
114
* @param structures Array of structures to write
115
*/
116
public static void autoWrite(Structure[] structures);
117
118
// Inner marker interfaces for parameter passing
119
120
/**
121
* Marker interface for pass-by-value structures
122
*/
123
public interface ByValue { }
124
125
/**
126
* Marker interface for pass-by-reference structures
127
*/
128
public interface ByReference { }
129
}
130
```
131
132
## Basic Structure Definition
133
134
```java { .api }
135
// Simple C structure mapping
136
public static class Point extends Structure {
137
public int x;
138
public int y;
139
140
public Point() {
141
super();
142
}
143
144
public Point(int x, int y) {
145
this.x = x;
146
this.y = y;
147
write(); // Write fields to native memory
148
}
149
150
public Point(Pointer p) {
151
super(p);
152
read(); // Read from existing memory
153
}
154
155
@Override
156
protected List<String> getFieldOrder() {
157
return Arrays.asList("x", "y");
158
}
159
160
@Override
161
public String toString() {
162
return String.format("Point{x=%d, y=%d}", x, y);
163
}
164
}
165
166
// Usage
167
Point p1 = new Point(10, 20); // Create and initialize
168
Point p2 = new Point(); // Create uninitialized
169
p2.x = 30; p2.y = 40; p2.write(); // Set fields and write
170
```
171
172
## Advanced Structure Features
173
174
```java { .api }
175
// Structure with various field types
176
public static class ComplexStruct extends Structure {
177
public byte byteField;
178
public short shortField;
179
public int intField;
180
public long longField;
181
public float floatField;
182
public double doubleField;
183
public Pointer ptrField;
184
public String stringField; // Converted to/from native string
185
186
// Array fields
187
public int[] intArray = new int[10];
188
public byte[] bytes = new byte[256];
189
190
// Nested structure
191
public Point location = new Point();
192
193
@Override
194
protected List<String> getFieldOrder() {
195
return Arrays.asList(
196
"byteField", "shortField", "intField", "longField",
197
"floatField", "doubleField", "ptrField", "stringField",
198
"intArray", "bytes", "location"
199
);
200
}
201
}
202
203
// Structure with custom field handling
204
public static class CustomStruct extends Structure {
205
public int flags;
206
public String name;
207
208
@Override
209
protected List<String> getFieldOrder() {
210
return Arrays.asList("flags", "name");
211
}
212
213
@Override
214
public void read() {
215
super.read();
216
// Custom post-read processing
217
if (name != null) {
218
name = name.trim();
219
}
220
}
221
222
@Override
223
public void write() {
224
// Custom pre-write processing
225
if (name != null && name.length() > 255) {
226
name = name.substring(0, 255);
227
}
228
super.write();
229
}
230
}
231
```
232
233
## Pass By Value vs By Reference
234
235
```java { .api }
236
// Pass by value - structure copied to native stack
237
public static class PointByValue extends Structure implements Structure.ByValue {
238
public int x, y;
239
240
@Override
241
protected List<String> getFieldOrder() {
242
return Arrays.asList("x", "y");
243
}
244
}
245
246
// Pass by reference - pointer to structure passed
247
public static class PointByReference extends Structure implements Structure.ByReference {
248
public int x, y;
249
250
@Override
251
protected List<String> getFieldOrder() {
252
return Arrays.asList("x", "y");
253
}
254
}
255
256
// Native function declarations
257
public interface Graphics extends Library {
258
/**
259
* Function expecting structure by value
260
* void drawPoint(Point point);
261
*/
262
void drawPoint(PointByValue point);
263
264
/**
265
* Function expecting structure by reference
266
* void updatePoint(Point* point);
267
*/
268
void updatePoint(PointByReference point);
269
}
270
271
// Usage
272
PointByValue p1 = new PointByValue();
273
p1.x = 10; p1.y = 20;
274
Graphics.INSTANCE.drawPoint(p1); // Structure copied
275
276
PointByReference p2 = new PointByReference();
277
p2.x = 30; p2.y = 40; p2.write();
278
Graphics.INSTANCE.updatePoint(p2); // Pointer passed
279
p2.read(); // Read any changes made by native function
280
```
281
282
## Union Class - Shared Memory Fields
283
284
```java { .api }
285
/**
286
* Represents a native union where all fields share memory
287
*/
288
public abstract class Union extends Structure {
289
/**
290
* Set active field type by class
291
* @param type Class of field to make active
292
*/
293
public void setType(Class<?> type);
294
295
/**
296
* Set active field type by field name
297
* @param fieldName Name of field to make active
298
*/
299
public void setType(String fieldName);
300
301
/**
302
* Read value as specific type
303
* @param type Type to read value as
304
* @return Value cast to specified type
305
*/
306
public Object getTypedValue(Class<?> type);
307
308
/**
309
* Set value and active type
310
* @param object Value to set (determines active type)
311
*/
312
public void setTypedValue(Object object);
313
}
314
315
// Union example
316
public static class IntOrFloat extends Union {
317
public int intValue;
318
public float floatValue;
319
320
@Override
321
protected List<String> getFieldOrder() {
322
return Arrays.asList("intValue", "floatValue");
323
}
324
}
325
326
// Usage
327
IntOrFloat u = new IntOrFloat();
328
u.setType("intValue"); // Set active type
329
u.intValue = 42; // Set int value
330
u.write(); // Write to memory
331
332
u.setType("floatValue"); // Change active type
333
u.read(); // Read as float
334
float f = u.floatValue; // Same memory, different interpretation
335
336
// Typed access
337
u.setTypedValue(3.14f); // Sets floatValue and active type
338
Object value = u.getTypedValue(Float.class); // Gets as float
339
```
340
341
## PointerType Class - Type-Safe Native Pointers
342
343
The `PointerType` abstract class provides a foundation for creating type-safe native pointer wrappers:
344
345
```java { .api }
346
/**
347
* Base class for creating type-safe native pointer types
348
*/
349
public abstract class PointerType implements NativeMapped {
350
/**
351
* Default constructor wraps a NULL pointer
352
*/
353
protected PointerType();
354
355
/**
356
* Constructor using existing pointer
357
* @param p Pointer to wrap
358
*/
359
protected PointerType(Pointer p);
360
361
/**
362
* Get the associated native pointer
363
* @return Native pointer representation
364
*/
365
public Pointer getPointer();
366
367
/**
368
* Set the associated native pointer
369
* @param p Pointer to set
370
*/
371
public void setPointer(Pointer p);
372
373
// NativeMapped implementation
374
public Class<?> nativeType();
375
public Object toNative();
376
public Object fromNative(Object nativeValue, FromNativeContext context);
377
378
// Object methods
379
public int hashCode();
380
public boolean equals(Object o);
381
public String toString();
382
}
383
```
384
385
### PointerType Usage Examples
386
387
```java
388
// Create custom pointer type for handles
389
public class WindowHandle extends PointerType {
390
public WindowHandle() { super(); }
391
public WindowHandle(Pointer p) { super(p); }
392
}
393
394
// Use in native library interface
395
public interface WindowsAPI extends Library {
396
WindowHandle CreateWindow(String className, String windowName, int style,
397
int x, int y, int width, int height,
398
WindowHandle parent, Pointer menu,
399
Pointer instance, Pointer param);
400
boolean DestroyWindow(WindowHandle window);
401
boolean ShowWindow(WindowHandle window, int cmdShow);
402
}
403
404
// Usage
405
WindowsAPI api = Native.loadLibrary("user32", WindowsAPI.class);
406
WindowHandle window = api.CreateWindow("STATIC", "Test", 0,
407
100, 100, 300, 200,
408
null, null, null, null);
409
api.ShowWindow(window, 1);
410
```
411
412
## Integer Types - Platform-Specific Sizes
413
414
```java { .api }
415
/**
416
* Base class for platform-specific integer types
417
*/
418
public abstract class IntegerType extends Number implements NativeMapped {
419
/**
420
* Create signed zero-valued integer
421
* @param size Size in bytes (1, 2, 4, or 8)
422
*/
423
public IntegerType(int size);
424
425
/**
426
* Create optionally unsigned integer
427
* @param size Size in bytes
428
* @param unsigned true for unsigned type
429
*/
430
public IntegerType(int size, boolean unsigned);
431
432
/**
433
* Create with specific value
434
* @param size Size in bytes
435
* @param value Initial value
436
* @param unsigned true for unsigned type
437
*/
438
public IntegerType(int size, long value, boolean unsigned);
439
440
/**
441
* Set the integer value
442
* @param value New value
443
*/
444
public void setValue(long value);
445
446
@Override
447
public int intValue();
448
449
@Override
450
public long longValue();
451
452
@Override
453
public float floatValue();
454
455
@Override
456
public double doubleValue();
457
458
// Static utility methods
459
460
/**
461
* Compare two IntegerType values
462
* @param v1 First value
463
* @param v2 Second value
464
* @return Comparison result (-1, 0, 1)
465
*/
466
public static int compare(IntegerType v1, IntegerType v2);
467
}
468
469
/**
470
* Platform-specific long integer (long in C, not Java long)
471
*/
472
public class NativeLong extends IntegerType {
473
/** Size of native long in bytes */
474
public static final int SIZE;
475
476
public NativeLong();
477
public NativeLong(long value);
478
}
479
480
// Custom integer types
481
public static class UInt32 extends IntegerType {
482
public UInt32() { super(4, true); } // 4-byte unsigned
483
public UInt32(long value) { super(4, value, true); }
484
}
485
486
public static class Int64 extends IntegerType {
487
public Int64() { super(8, false); } // 8-byte signed
488
public Int64(long value) { super(8, value, false); }
489
}
490
```
491
492
## Type Mapping System
493
494
```java { .api }
495
/**
496
* Interface for custom Java to native type conversion
497
*/
498
public interface NativeMapped {
499
/**
500
* Convert from native representation to Java object
501
* @param nativeValue Native value
502
* @param context Conversion context
503
* @return Java object
504
*/
505
Object fromNative(Object nativeValue, FromNativeContext context);
506
507
/**
508
* Convert Java object to native representation
509
* @return Native value
510
*/
511
Object toNative();
512
513
/**
514
* Get native type class
515
* @return Class representing native type
516
*/
517
Class<?> nativeType();
518
}
519
520
/**
521
* Provides type converters for automatic conversion
522
*/
523
public interface TypeMapper {
524
/**
525
* Get converter for native-to-Java conversion
526
* @param javaType Target Java type
527
* @return Converter or null if none available
528
*/
529
FromNativeConverter getFromNativeConverter(Class<?> javaType);
530
531
/**
532
* Get converter for Java-to-native conversion
533
* @param javaType Source Java type
534
* @return Converter or null if none available
535
*/
536
ToNativeConverter getToNativeConverter(Class<?> javaType);
537
}
538
539
/**
540
* Default TypeMapper implementation
541
*/
542
public class DefaultTypeMapper implements TypeMapper {
543
/**
544
* Add bidirectional type converter
545
* @param javaType Java type to convert
546
* @param converter Bidirectional converter
547
*/
548
public void addTypeConverter(Class<?> javaType, TypeConverter converter);
549
550
/**
551
* Add from-native converter
552
* @param javaType Target Java type
553
* @param converter From-native converter
554
*/
555
public void addFromNativeConverter(Class<?> javaType, FromNativeConverter converter);
556
557
/**
558
* Add to-native converter
559
* @param javaType Source Java type
560
* @param converter To-native converter
561
*/
562
public void addToNativeConverter(Class<?> javaType, ToNativeConverter converter);
563
}
564
```
565
566
## Custom Type Examples
567
568
```java { .api }
569
// Boolean type that maps to int (common in C)
570
public static class CBoolean implements NativeMapped {
571
private final boolean value;
572
573
public CBoolean(boolean value) {
574
this.value = value;
575
}
576
577
public boolean booleanValue() {
578
return value;
579
}
580
581
@Override
582
public Object fromNative(Object nativeValue, FromNativeContext context) {
583
return new CBoolean(((Integer) nativeValue) != 0);
584
}
585
586
@Override
587
public Object toNative() {
588
return value ? 1 : 0;
589
}
590
591
@Override
592
public Class<?> nativeType() {
593
return Integer.class;
594
}
595
}
596
597
// Enum mapping to native constants
598
public enum Status implements NativeMapped {
599
OK(0), ERROR(1), PENDING(2);
600
601
private final int value;
602
603
Status(int value) {
604
this.value = value;
605
}
606
607
public int getValue() {
608
return value;
609
}
610
611
@Override
612
public Object fromNative(Object nativeValue, FromNativeContext context) {
613
int val = (Integer) nativeValue;
614
for (Status status : values()) {
615
if (status.value == val) return status;
616
}
617
throw new IllegalArgumentException("Invalid status: " + val);
618
}
619
620
@Override
621
public Object toNative() {
622
return value;
623
}
624
625
@Override
626
public Class<?> nativeType() {
627
return Integer.class;
628
}
629
}
630
631
// Using custom types in library interface
632
public interface MyLibrary extends Library {
633
void setEnabled(CBoolean enabled); // Converts boolean <-> int
634
Status getStatus(); // Converts int <-> Status enum
635
}
636
```
637
638
## Callback System
639
640
```java { .api }
641
/**
642
* Base interface for all native callbacks
643
*/
644
public interface Callback {
645
/** Default method name for single-method callbacks */
646
String METHOD_NAME = "callback";
647
648
/** Forbidden method names that cannot be callback methods */
649
String[] FORBIDDEN_NAMES = {"hashCode", "equals", "toString"};
650
651
/**
652
* Handler for uncaught exceptions in callback threads
653
*/
654
public interface UncaughtExceptionHandler {
655
void uncaughtException(Callback c, Throwable e);
656
}
657
}
658
659
// Simple callback examples
660
public interface SimpleCallback extends Callback {
661
void callback(int value);
662
}
663
664
public interface ComparisonCallback extends Callback {
665
int compare(Pointer a, Pointer b);
666
}
667
668
// Multi-method callback (must use METHOD_NAME)
669
public interface FileCallback extends Callback {
670
boolean callback(String filename, int attributes);
671
}
672
```
673
674
## Callback Usage Examples
675
676
```java { .api }
677
// Define library with callback functions
678
public interface CallbackLibrary extends Library {
679
CallbackLibrary INSTANCE = Native.loadLibrary("mylib", CallbackLibrary.class);
680
681
/**
682
* Register callback for notifications
683
* void setNotificationCallback(void (*callback)(int));
684
*/
685
void setNotificationCallback(SimpleCallback callback);
686
687
/**
688
* Sort array using comparison function
689
* void qsort(void* base, size_t num, size_t size,
690
* int (*compar)(const void*, const void*));
691
*/
692
void qsort(Pointer array, int count, int elementSize, ComparisonCallback comparator);
693
694
/**
695
* Enumerate files with callback
696
* int enumFiles(const char* path, bool (*callback)(const char*, int));
697
*/
698
int enumFiles(String path, FileCallback callback);
699
}
700
701
// Implement callbacks
702
SimpleCallback notifier = new SimpleCallback() {
703
@Override
704
public void callback(int value) {
705
System.out.println("Notification received: " + value);
706
}
707
};
708
709
ComparisonCallback intComparator = new ComparisonCallback() {
710
@Override
711
public int compare(Pointer a, Pointer b) {
712
int val1 = a.getInt(0);
713
int val2 = b.getInt(0);
714
return Integer.compare(val1, val2);
715
}
716
};
717
718
FileCallback fileHandler = new FileCallback() {
719
@Override
720
public boolean callback(String filename, int attributes) {
721
System.out.println("Found file: " + filename + " (attrs: " + attributes + ")");
722
return true; // Continue enumeration
723
}
724
};
725
726
// Register and use callbacks
727
CallbackLibrary.INSTANCE.setNotificationCallback(notifier);
728
729
// Sort integer array
730
Memory intArray = new Memory(40); // 10 integers
731
for (int i = 0; i < 10; i++) {
732
intArray.setInt(i * 4, (int)(Math.random() * 100));
733
}
734
CallbackLibrary.INSTANCE.qsort(intArray, 10, 4, intComparator);
735
736
// Enumerate files
737
CallbackLibrary.INSTANCE.enumFiles("/path/to/directory", fileHandler);
738
```
739
740
## Advanced Callback Features
741
742
```java { .api }
743
/**
744
* Callback thread initialization
745
*/
746
public class CallbackThreadInitializer {
747
/**
748
* Get thread name for callback
749
* @param cb Callback instance
750
* @return Thread name
751
*/
752
public String getName(Callback cb);
753
754
/**
755
* Get thread group for callback
756
* @param cb Callback instance
757
* @return Thread group
758
*/
759
public ThreadGroup getGroup(Callback cb);
760
761
/**
762
* Check if callback thread should be daemon
763
* @param cb Callback instance
764
* @return true if daemon thread
765
*/
766
public boolean isDaemon(Callback cb);
767
768
/**
769
* Get uncaught exception handler
770
* @param cb Callback instance
771
* @return Exception handler
772
*/
773
public Thread.UncaughtExceptionHandler getUncaughtExceptionHandler(Callback cb);
774
}
775
776
// Callback with exception handling
777
SimpleCallback robustCallback = new SimpleCallback() {
778
@Override
779
public void callback(int value) {
780
try {
781
// Process value...
782
if (value < 0) {
783
throw new IllegalArgumentException("Negative value not allowed");
784
}
785
System.out.println("Processing: " + value);
786
} catch (Exception e) {
787
System.err.println("Callback error: " + e.getMessage());
788
// Don't let exceptions propagate to native code
789
}
790
}
791
};
792
```
793
794
## Function Pointer Management
795
796
```java { .api }
797
/**
798
* Manages callback references and function pointers
799
*/
800
public class CallbackReference extends WeakReference<Callback> {
801
/**
802
* Get callback instance from function pointer
803
* @param type Callback interface type
804
* @param p Function pointer
805
* @return Callback instance or null
806
*/
807
public static Callback getCallback(Class<?> type, Pointer p);
808
809
/**
810
* Get function pointer for callback
811
* @param callback Callback instance
812
* @return Native function pointer
813
*/
814
public static Pointer getFunctionPointer(Callback callback);
815
}
816
817
// Get function pointer for callback
818
Pointer funcPtr = CallbackReference.getFunctionPointer(myCallback);
819
820
// Pass function pointer directly to native code
821
someNativeFunction(funcPtr);
822
823
// Retrieve callback from pointer (if needed)
824
Callback retrieved = CallbackReference.getCallback(SimpleCallback.class, funcPtr);
825
```
826
827
## Structure Arrays and Complex Layouts
828
829
```java { .api }
830
// Array of structures
831
Point[] pointArray = (Point[]) new Point().toArray(10);
832
for (int i = 0; i < pointArray.length; i++) {
833
pointArray[i].x = i * 10;
834
pointArray[i].y = i * 20;
835
}
836
Structure.autoWrite(pointArray); // Write all structures
837
838
// Pass to native function expecting Point*
839
void processPoints(Point[] points);
840
841
// After native call, read updated values
842
Structure.autoRead(pointArray);
843
844
// Nested structures
845
public static class Rectangle extends Structure {
846
public Point topLeft = new Point();
847
public Point bottomRight = new Point();
848
849
@Override
850
protected List<String> getFieldOrder() {
851
return Arrays.asList("topLeft", "bottomRight");
852
}
853
}
854
855
// Variable-length structure (common C pattern)
856
public static class VariableStruct extends Structure {
857
public int count;
858
public int[] data;
859
860
public VariableStruct(int count) {
861
this.count = count;
862
this.data = new int[count];
863
}
864
865
@Override
866
protected List<String> getFieldOrder() {
867
return Arrays.asList("count", "data");
868
}
869
}
870
```
871
872
## Best Practices and Error Handling
873
874
```java { .api }
875
// Always implement getFieldOrder()
876
@Override
877
protected List<String> getFieldOrder() {
878
return Arrays.asList("field1", "field2", "field3");
879
// Order MUST match C structure layout
880
}
881
882
// Handle encoding for string fields
883
public static class TextStruct extends Structure {
884
public String text;
885
886
public TextStruct() {
887
super();
888
setStringEncoding("UTF-8"); // Explicit encoding
889
}
890
}
891
892
// Validate structure after read
893
@Override
894
public void read() {
895
super.read();
896
897
// Validate field values
898
if (count < 0 || count > MAX_COUNT) {
899
throw new IllegalStateException("Invalid count: " + count);
900
}
901
902
// Null-check string fields
903
if (name == null) {
904
name = "";
905
}
906
}
907
908
// Handle memory alignment issues
909
public static class AlignedStruct extends Structure {
910
public byte b; // 1 byte
911
// 3 bytes padding
912
public int i; // 4 bytes
913
public short s; // 2 bytes
914
// 2 bytes padding (to align to 4-byte boundary)
915
916
@Override
917
protected List<String> getFieldOrder() {
918
return Arrays.asList("b", "i", "s");
919
}
920
}
921
```
922
923
## WString Class - Wide Character String Support
924
925
The `WString` class provides Unicode wide character string support for native libraries:
926
927
```java { .api }
928
/**
929
* Wrapper for wide character strings (wchar_t* in C)
930
*/
931
public final class WString implements CharSequence, Comparable {
932
/**
933
* Create wide string from regular string
934
* @param s String to wrap
935
*/
936
public WString(String s);
937
938
// CharSequence implementation
939
public int length();
940
public char charAt(int index);
941
public CharSequence subSequence(int start, int end);
942
943
// String operations
944
public String toString();
945
public boolean equals(Object o);
946
public int hashCode();
947
public int compareTo(Object o);
948
}
949
```
950
951
### WString Usage Examples
952
953
```java
954
// Use WString for wide character APIs
955
public interface UnicodeAPI extends Library {
956
UnicodeAPI INSTANCE = Native.loadLibrary("mylib", UnicodeAPI.class);
957
958
/**
959
* Process wide character string
960
* @param text Wide character string input
961
* @return Length of processed string
962
*/
963
int processWideString(WString text);
964
965
/**
966
* Create wide character string
967
* @param buffer Buffer to fill with wide string
968
* @param maxLen Maximum length
969
* @return Actual length
970
*/
971
int createWideString(WString buffer, int maxLen);
972
}
973
974
// Usage
975
WString wideText = new WString("Hello Unicode: ñáéíóú");
976
int result = UnicodeAPI.INSTANCE.processWideString(wideText);
977
978
// Wide strings work with Windows APIs
979
Function messageBoxW = Function.getFunction("user32", "MessageBoxW", Function.ALT_CONVENTION);
980
messageBoxW.invoke(Integer.class, new Object[]{
981
Pointer.NULL, new WString("Unicode Message"), new WString("Title"), 0
982
});
983
```
984
985
## StringArray Class - Native String Arrays
986
987
The `StringArray` class manages native arrays of string pointers with automatic cleanup:
988
989
```java { .api }
990
/**
991
* Handle native array of char* or wchar_t* with automatic memory management
992
*/
993
public class StringArray extends Memory implements Function.PostCallRead {
994
/**
995
* Create native array of strings
996
* @param strings Java string array
997
*/
998
public StringArray(String[] strings);
999
1000
/**
1001
* Create wide character string array
1002
* @param strings Java string array
1003
* @param wide true for wide character strings
1004
*/
1005
public StringArray(String[] strings, boolean wide);
1006
1007
/**
1008
* Create string array with specific encoding
1009
* @param strings Java string array
1010
* @param encoding Character encoding
1011
*/
1012
public StringArray(String[] strings, String encoding);
1013
1014
/**
1015
* Create from WString array
1016
* @param strings WString array
1017
*/
1018
public StringArray(WString[] strings);
1019
1020
/**
1021
* Get updated string values after native call
1022
* @return String array with current values
1023
*/
1024
public String[] getStrings();
1025
1026
// PostCallRead implementation
1027
public void read();
1028
}
1029
```
1030
1031
### StringArray Usage Examples
1032
1033
```java
1034
// Native function expecting char** array
1035
public interface SystemAPI extends Library {
1036
SystemAPI INSTANCE = Native.loadLibrary("system", SystemAPI.class);
1037
1038
/**
1039
* Execute command with arguments
1040
* @param command Command name
1041
* @param args Null-terminated argument array
1042
* @return Exit code
1043
*/
1044
int execv(String command, StringArray args);
1045
1046
/**
1047
* Process environment variables
1048
* @param envVars Environment variable array
1049
*/
1050
void processEnvironment(StringArray envVars);
1051
}
1052
1053
// Usage
1054
String[] cmdArgs = {"/bin/ls", "-la", "/tmp", null}; // NULL-terminated
1055
StringArray nativeArgs = new StringArray(cmdArgs);
1056
1057
int exitCode = SystemAPI.INSTANCE.execv("/bin/ls", nativeArgs);
1058
1059
// Wide character string arrays for Windows
1060
String[] paths = {"C:\\Program Files", "C:\\Windows", "C:\\Users"};
1061
StringArray widePaths = new StringArray(paths, true); // Wide strings
1062
1063
// String array with custom encoding
1064
String[] utf8Strings = {"ñáéíóú", "中文", "العربية"};
1065
StringArray encodedStrings = new StringArray(utf8Strings, "UTF-8");
1066
```
1067
1068
## Type Conversion System
1069
1070
### TypeMapper Interface - Custom Type Conversion
1071
1072
```java { .api }
1073
/**
1074
* Provides converters for Java to native type conversion
1075
*/
1076
public interface TypeMapper {
1077
/**
1078
* Get converter from native to Java type
1079
* @param javaType Target Java type
1080
* @return Converter from native type
1081
*/
1082
FromNativeConverter getFromNativeConverter(Class<?> javaType);
1083
1084
/**
1085
* Get converter from Java to native type
1086
* @param javaType Source Java type
1087
* @return Converter to native type
1088
*/
1089
ToNativeConverter getToNativeConverter(Class<?> javaType);
1090
}
1091
```
1092
1093
### NativeMapped Interface - Bidirectional Type Conversion
1094
1095
```java { .api }
1096
/**
1097
* Interface for objects that can convert between Java and native types
1098
*/
1099
public interface NativeMapped {
1100
/**
1101
* Convert from native value to Java object
1102
* @param nativeValue Native value to convert
1103
* @param context Conversion context
1104
* @return Java object representation
1105
*/
1106
Object fromNative(Object nativeValue, FromNativeContext context);
1107
1108
/**
1109
* Convert this object to native value
1110
* @return Native representation
1111
*/
1112
Object toNative();
1113
1114
/**
1115
* Get the native type used for conversion
1116
* @return Native type class
1117
*/
1118
Class<?> nativeType();
1119
}
1120
```
1121
1122
### Custom Type Conversion Examples
1123
1124
```java
1125
// Custom enum with native mapping
1126
public enum Color implements NativeMapped {
1127
RED(1), GREEN(2), BLUE(3);
1128
1129
private final int value;
1130
1131
Color(int value) {
1132
this.value = value;
1133
}
1134
1135
@Override
1136
public Object toNative() {
1137
return value;
1138
}
1139
1140
@Override
1141
public Object fromNative(Object nativeValue, FromNativeContext context) {
1142
int val = (Integer) nativeValue;
1143
for (Color color : values()) {
1144
if (color.value == val) return color;
1145
}
1146
throw new IllegalArgumentException("Unknown color: " + val);
1147
}
1148
1149
@Override
1150
public Class<?> nativeType() {
1151
return Integer.class;
1152
}
1153
}
1154
1155
// TypeMapper for custom conversions
1156
public class CustomTypeMapper implements TypeMapper {
1157
@Override
1158
public FromNativeConverter getFromNativeConverter(Class<?> javaType) {
1159
if (javaType == Color.class) {
1160
return new FromNativeConverter() {
1161
@Override
1162
public Object fromNative(Object nativeValue, FromNativeContext context) {
1163
return Color.values()[(Integer) nativeValue - 1];
1164
}
1165
1166
@Override
1167
public Class<?> nativeType() {
1168
return Integer.class;
1169
}
1170
};
1171
}
1172
return null;
1173
}
1174
1175
@Override
1176
public ToNativeConverter getToNativeConverter(Class<?> javaType) {
1177
if (javaType == Color.class) {
1178
return new ToNativeConverter() {
1179
@Override
1180
public Object toNative(Object value, ToNativeContext context) {
1181
return ((Color) value).value;
1182
}
1183
1184
@Override
1185
public Class<?> nativeType() {
1186
return Integer.class;
1187
}
1188
};
1189
}
1190
return null;
1191
}
1192
}
1193
1194
// Use custom type mapper in library
1195
Map<String, Object> options = new HashMap<>();
1196
options.put(Library.OPTION_TYPE_MAPPER, new CustomTypeMapper());
1197
1198
MyLibrary lib = Native.loadLibrary("mylib", MyLibrary.class, options);
1199
1200
// Now Color enum works automatically
1201
Color result = lib.getPreferredColor(); // Returns Color enum
1202
lib.setBackgroundColor(Color.BLUE); // Accepts Color enum
1203
```
1204
1205
This comprehensive type system enables seamless integration between Java objects and native data structures, providing automatic memory layout, type conversion, and callback support for complex native library integration.