node-ctypes 1.5.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +149 -777
- package/build/Release/ctypes-darwin-arm64.node +0 -0
- package/build/Release/ctypes-darwin-x64.node +0 -0
- package/build/Release/ctypes-linux-arm64.node +0 -0
- package/build/Release/ctypes-linux-x64.node +0 -0
- package/build/Release/ctypes-win32-arm64.node +0 -0
- package/build/Release/ctypes-win32-x64.node +0 -0
- package/lib/index.d.ts +1140 -58
- package/lib/index.js +24 -0
- package/lib/memory/pointer.js +33 -0
- package/lib/structures/helpers/struct.js +88 -0
- package/lib/structures/helpers/union.js +7 -0
- package/lib/types/primitives.js +16 -0
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -5,907 +5,282 @@
|
|
|
5
5
|
[](https://github.com/dmazzella/node-ctypes/actions/workflows/build.yml)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
|
-
**Python ctypes for Node.js**
|
|
8
|
+
**Python ctypes for Node.js** — A foreign function interface library that mirrors the Python ctypes API, built on [libffi](https://github.com/libffi/libffi) and [N-API](https://nodejs.org/api/n-api.html).
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- ✨ **Python ctypes API Compatibility** - If you know Python ctypes, you already know node-ctypes! Same syntax, same patterns.
|
|
13
|
-
- 🚀 **High Performance** - Up to **50x faster** than ffi-napi, with struct operations matching or exceeding koffi performance.
|
|
14
|
-
- 🔧 **Complete FFI Support** - Structs, unions, bit fields, nested structures, arrays, callbacks, variadic functions, and more.
|
|
15
|
-
- 🌍 **Cross-platform** - Works seamlessly on Linux, macOS, and Windows with identical API.
|
|
16
|
-
|
|
17
|
-
## Features
|
|
18
|
-
|
|
19
|
-
- 🐍 **Full Python ctypes compatibility** - Struct definitions, unions, bit fields, anonymous fields
|
|
20
|
-
- 📚 **Load shared libraries** (.so, .dll, .dylib) with CDLL and WinDLL
|
|
21
|
-
- 🔄 **Variadic functions** (printf, sprintf) with automatic detection
|
|
22
|
-
- 📞 **Callbacks** - JavaScript functions callable from C code
|
|
23
|
-
- 🏗️ **Complex data structures** - Nested structs, unions in structs, arrays in structs
|
|
24
|
-
- ⚡ **High performance** - Eager loading for struct properties, optimized FFI wrapper
|
|
25
|
-
- 🔍 **Transparent API** - Pass struct objects directly to FFI functions
|
|
26
|
-
- 🔧 **Extended type support** - All ctypes types (int8, uint8, int16, uint16, etc.)
|
|
27
|
-
- 📊 **Memory utilities** - readValue/writeValue for direct memory access, enhanced sizeof
|
|
28
|
-
- 🏗️ **Advanced array support** - String initialization, improved proxy behavior
|
|
10
|
+
If you know Python ctypes, you already know node-ctypes.
|
|
29
11
|
|
|
30
12
|
## Installation
|
|
31
13
|
|
|
32
|
-
### From npm (recommended)
|
|
33
|
-
|
|
34
14
|
```bash
|
|
35
15
|
npm install node-ctypes
|
|
36
16
|
```
|
|
37
17
|
|
|
38
|
-
Prebuilt binaries
|
|
39
|
-
- Windows x64, ARM64
|
|
40
|
-
- Linux x64, ARM64
|
|
41
|
-
- macOS x64, ARM64
|
|
18
|
+
Prebuilt binaries for Windows, Linux, and macOS (x64/ARM64).
|
|
42
19
|
|
|
43
|
-
|
|
20
|
+
<details>
|
|
21
|
+
<summary>Build from source</summary>
|
|
44
22
|
|
|
45
|
-
|
|
23
|
+
Requires Node.js >= 16, CMake >= 3.15, and a C++ compiler.
|
|
46
24
|
|
|
47
|
-
- Node.js >= 16
|
|
48
|
-
- CMake >= 3.15
|
|
49
|
-
- C++ compiler (GCC, Clang, or MSVC)
|
|
50
|
-
|
|
51
|
-
#### Ubuntu/Debian
|
|
52
|
-
```bash
|
|
53
|
-
sudo apt install build-essential cmake
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
#### macOS
|
|
57
|
-
```bash
|
|
58
|
-
brew install cmake
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
#### Windows
|
|
62
|
-
- Install Visual Studio Build Tools
|
|
63
|
-
- Install CMake
|
|
64
|
-
|
|
65
|
-
#### Build
|
|
66
25
|
```bash
|
|
67
26
|
npm install
|
|
68
27
|
npm run build
|
|
69
28
|
```
|
|
29
|
+
</details>
|
|
70
30
|
|
|
71
|
-
##
|
|
31
|
+
## Python ctypes vs node-ctypes
|
|
72
32
|
|
|
73
|
-
|
|
33
|
+
### Loading libraries and calling functions
|
|
74
34
|
|
|
75
|
-
**Python
|
|
35
|
+
**Python:**
|
|
76
36
|
```python
|
|
77
|
-
from ctypes import CDLL, c_int
|
|
37
|
+
from ctypes import CDLL, c_int
|
|
78
38
|
|
|
79
39
|
libc = CDLL("libc.so.6")
|
|
80
40
|
abs_func = libc.abs
|
|
81
41
|
abs_func.argtypes = [c_int]
|
|
82
42
|
abs_func.restype = c_int
|
|
83
43
|
print(abs_func(-42)) # 42
|
|
84
|
-
|
|
85
|
-
class Point(Structure):
|
|
86
|
-
_fields_ = [("x", c_int), ("y", c_int)]
|
|
87
|
-
|
|
88
|
-
p = Point(10, 20)
|
|
89
|
-
print(p.x, p.y) # 10 20
|
|
90
44
|
```
|
|
91
45
|
|
|
92
|
-
**
|
|
46
|
+
**Node.js:**
|
|
93
47
|
```javascript
|
|
94
|
-
import { CDLL, c_int
|
|
48
|
+
import { CDLL, c_int } from 'node-ctypes';
|
|
95
49
|
|
|
96
|
-
|
|
97
|
-
const libc = new CDLL("libc.so.6"); // Linux
|
|
50
|
+
const libc = new CDLL("libc.so.6"); // Linux
|
|
98
51
|
// const libc = new CDLL('msvcrt.dll'); // Windows
|
|
99
52
|
// const libc = new CDLL('libc.dylib'); // macOS
|
|
100
|
-
|
|
101
|
-
const abs = libc.func("abs", c_int, [c_int]);
|
|
102
|
-
console.log(abs(-42)); // 42
|
|
103
|
-
|
|
104
|
-
// Python ctypes-like syntax
|
|
105
53
|
const abs_func = libc.abs;
|
|
106
54
|
abs_func.argtypes = [c_int];
|
|
107
55
|
abs_func.restype = c_int;
|
|
108
56
|
console.log(abs_func(-42)); // 42
|
|
109
|
-
|
|
110
|
-
class Point extends Structure {
|
|
111
|
-
static _fields_ = [
|
|
112
|
-
["x", c_int],
|
|
113
|
-
["y", c_int]
|
|
114
|
-
];
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const p = new Point(10, 20);
|
|
118
|
-
console.log(p.x, p.y); // 10 20
|
|
119
57
|
```
|
|
120
58
|
|
|
121
|
-
|
|
59
|
+
A traditional syntax is also available: `libc.func("abs", c_int, [c_int])`.
|
|
122
60
|
|
|
123
|
-
###
|
|
124
|
-
|
|
125
|
-
```javascript
|
|
126
|
-
import { CDLL, c_int, c_double, c_char_p, c_size_t } from 'node-ctypes';
|
|
127
|
-
|
|
128
|
-
// Load libc
|
|
129
|
-
const libc = new CDLL('libc.so.6'); // Linux
|
|
130
|
-
// const libc = new CDLL('msvcrt.dll'); // Windows
|
|
131
|
-
// const libc = new CDLL('libc.dylib'); // macOS
|
|
132
|
-
|
|
133
|
-
// Traditional syntax
|
|
134
|
-
const abs = libc.func('abs', c_int, [c_int]);
|
|
135
|
-
console.log(abs(-42)); // 42
|
|
136
|
-
|
|
137
|
-
// Python ctypes-like syntax (equivalent!)
|
|
138
|
-
const abs_func = libc.abs;
|
|
139
|
-
abs_func.argtypes = [c_int];
|
|
140
|
-
abs_func.restype = c_int;
|
|
141
|
-
console.log(abs_func(-42)); // 42
|
|
61
|
+
### Structures
|
|
142
62
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
63
|
+
**Python:**
|
|
64
|
+
```python
|
|
65
|
+
from ctypes import Structure, c_int, c_uint32
|
|
146
66
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
// const libb = new CDLL('ucrtbase.dll'); // Windows
|
|
150
|
-
// const libm = new CDLL('libm.dylib'); // macOS
|
|
67
|
+
class Point(Structure):
|
|
68
|
+
_fields_ = [("x", c_int), ("y", c_int)]
|
|
151
69
|
|
|
152
|
-
|
|
153
|
-
|
|
70
|
+
p = Point(10, 20)
|
|
71
|
+
print(p.x, p.y) # 10 20
|
|
154
72
|
```
|
|
155
73
|
|
|
156
|
-
|
|
157
|
-
|
|
74
|
+
**Node.js:**
|
|
158
75
|
```javascript
|
|
159
76
|
import { Structure, c_int, c_uint32 } from 'node-ctypes';
|
|
160
77
|
|
|
161
|
-
// Simple struct - Python-like class syntax
|
|
162
78
|
class Point extends Structure {
|
|
163
|
-
static _fields_ = [
|
|
164
|
-
["x", c_int],
|
|
165
|
-
["y", c_int]
|
|
166
|
-
];
|
|
79
|
+
static _fields_ = [["x", c_int], ["y", c_int]];
|
|
167
80
|
}
|
|
168
81
|
|
|
169
|
-
// Create and initialize - direct property access!
|
|
170
82
|
const p = new Point(10, 20);
|
|
171
83
|
console.log(p.x, p.y); // 10 20
|
|
172
|
-
|
|
173
|
-
// Modify properties directly
|
|
174
|
-
p.x = 100;
|
|
175
|
-
console.log(p.x); // 100
|
|
176
|
-
|
|
177
|
-
// Get struct size
|
|
178
|
-
console.log(Point.size); // 8
|
|
179
|
-
|
|
180
|
-
// Nested structs
|
|
181
|
-
class Rectangle extends Structure {
|
|
182
|
-
static _fields_ = [
|
|
183
|
-
["topLeft", Point],
|
|
184
|
-
["bottomRight", Point],
|
|
185
|
-
["color", c_uint32]
|
|
186
|
-
];
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const rect = new Rectangle({
|
|
190
|
-
topLeft: { x: 0, y: 0 },
|
|
191
|
-
bottomRight: { x: 100, y: 200 },
|
|
192
|
-
color: 0xff0000
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
console.log(rect.topLeft.x); // 0
|
|
196
|
-
console.log(rect.bottomRight.x); // 100
|
|
197
|
-
console.log(rect.color); // 16711680
|
|
198
84
|
```
|
|
199
85
|
|
|
200
|
-
|
|
86
|
+
Nested structs, unions, bit fields, and anonymous fields all work the same way.
|
|
87
|
+
|
|
88
|
+
### Unions
|
|
201
89
|
|
|
202
90
|
```javascript
|
|
203
91
|
import { Union, c_int, c_float } from 'node-ctypes';
|
|
204
92
|
|
|
205
|
-
// Union - all fields share the same memory
|
|
206
93
|
class IntOrFloat extends Union {
|
|
207
|
-
static _fields_ = [
|
|
208
|
-
["i", c_int],
|
|
209
|
-
["f", c_float]
|
|
210
|
-
];
|
|
94
|
+
static _fields_ = [["i", c_int], ["f", c_float]];
|
|
211
95
|
}
|
|
212
96
|
|
|
213
97
|
const u = new IntOrFloat();
|
|
214
98
|
u.f = 3.14159;
|
|
215
99
|
console.log(u.i); // Bit pattern of float as integer
|
|
216
|
-
|
|
217
|
-
u.i = 42;
|
|
218
|
-
console.log(u.f); // 42 reinterpreted as float
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
### Bit Fields - Compact Data Structures
|
|
222
|
-
|
|
223
|
-
```javascript
|
|
224
|
-
import { Structure, c_uint32 } from 'node-ctypes';
|
|
225
|
-
|
|
226
|
-
// Bit fields using Python-style syntax: [name, type, bits]
|
|
227
|
-
class Flags extends Structure {
|
|
228
|
-
static _fields_ = [
|
|
229
|
-
["enabled", c_uint32, 1], // 1 bit
|
|
230
|
-
["mode", c_uint32, 3], // 3 bits
|
|
231
|
-
["priority", c_uint32, 4], // 4 bits
|
|
232
|
-
["reserved", c_uint32, 24] // 24 bits
|
|
233
|
-
];
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const flags = new Flags();
|
|
237
|
-
|
|
238
|
-
flags.enabled = 1;
|
|
239
|
-
flags.mode = 5;
|
|
240
|
-
flags.priority = 12;
|
|
241
|
-
|
|
242
|
-
console.log(flags.enabled); // 1
|
|
243
|
-
console.log(flags.mode); // 5
|
|
244
|
-
console.log(flags.priority); // 12
|
|
245
100
|
```
|
|
246
101
|
|
|
247
|
-
|
|
102
|
+
### Arrays
|
|
248
103
|
|
|
249
104
|
```javascript
|
|
250
|
-
import {
|
|
105
|
+
import { array, c_int32 } from 'node-ctypes';
|
|
251
106
|
|
|
252
|
-
class Flags extends Structure {
|
|
253
|
-
static _fields_ = [
|
|
254
|
-
["enabled", bitfield(c_uint32, 1)],
|
|
255
|
-
["mode", bitfield(c_uint32, 3)],
|
|
256
|
-
];
|
|
257
|
-
}
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
### Arrays - Fixed-size and Dynamic
|
|
261
|
-
|
|
262
|
-
```javascript
|
|
263
|
-
import { c_int32, c_uint8, array } from 'node-ctypes';
|
|
264
|
-
|
|
265
|
-
// Fixed-size array
|
|
266
107
|
const IntArray = array(c_int32, 5);
|
|
267
108
|
const arr = IntArray.create([1, 2, 3, 4, 5]);
|
|
268
|
-
|
|
269
|
-
// Array access
|
|
270
109
|
console.log(arr[0]); // 1
|
|
271
|
-
console.log(arr[4]); // 5
|
|
272
|
-
|
|
273
|
-
// Iterate
|
|
274
|
-
for (const val of arr) {
|
|
275
|
-
console.log(val);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Arrays in structs
|
|
279
|
-
import { Structure, array, c_uint8 } from 'node-ctypes';
|
|
280
|
-
|
|
281
|
-
class Packet extends Structure {
|
|
282
|
-
static _fields_ = [
|
|
283
|
-
["header", array(c_uint8, 8)],
|
|
284
|
-
["data", array(c_uint8, 256)]
|
|
285
|
-
];
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const pkt = new Packet({
|
|
289
|
-
header: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
|
|
290
|
-
data: new Array(256).fill(0)
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
console.log(pkt.header.toString()); // [1, 2, 3, 4, 5, 6, 7, 8]
|
|
294
110
|
```
|
|
295
111
|
|
|
296
|
-
###
|
|
297
|
-
|
|
298
|
-
Real-world example from our test suite:
|
|
112
|
+
### Pointers
|
|
299
113
|
|
|
300
114
|
```javascript
|
|
301
|
-
import {
|
|
115
|
+
import { POINTER, pointer, c_int32 } from 'node-ctypes';
|
|
302
116
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
["r", c_uint8],
|
|
307
|
-
["g", c_uint8],
|
|
308
|
-
["b", c_uint8],
|
|
309
|
-
["a", c_uint8]
|
|
310
|
-
];
|
|
311
|
-
}
|
|
117
|
+
const IntPtr = POINTER(c_int32);
|
|
118
|
+
const buf = Buffer.alloc(4);
|
|
119
|
+
buf.writeInt32LE(42, 0);
|
|
312
120
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
["rgb", RGB],
|
|
317
|
-
["value", c_int32]
|
|
318
|
-
];
|
|
319
|
-
}
|
|
121
|
+
const p = IntPtr.fromBuffer(buf);
|
|
122
|
+
console.log(p.contents); // 42 (like *p in C)
|
|
123
|
+
console.log(p[0]); // 42 (like p[0] in C)
|
|
320
124
|
|
|
321
|
-
//
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
["y", c_uint16],
|
|
326
|
-
["color", Color]
|
|
327
|
-
];
|
|
328
|
-
}
|
|
125
|
+
// pointer() function
|
|
126
|
+
const x = new c_int32(42);
|
|
127
|
+
const px = pointer(x);
|
|
128
|
+
console.log(px.contents); // 42
|
|
329
129
|
|
|
330
|
-
//
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
["height", c_uint16],
|
|
335
|
-
["pixels", array(Pixel, 2)]
|
|
336
|
-
];
|
|
337
|
-
}
|
|
130
|
+
// fromAddress() — typed access to native memory
|
|
131
|
+
const pValues = POINTER(MyStruct).fromAddress(nativeAddr);
|
|
132
|
+
console.log(pValues[0].field); // pointer arithmetic (like pValues[0] in C)
|
|
133
|
+
console.log(pValues[5].field); // pValues + 5 * sizeof(MyStruct)
|
|
338
134
|
|
|
339
|
-
//
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
height: 480,
|
|
343
|
-
pixels: [
|
|
344
|
-
{ x: 10, y: 20, color: { rgb: { r: 255, g: 0, b: 0, a: 255 } } },
|
|
345
|
-
{ x: 30, y: 40, color: { value: 0xFF00FF00 } }
|
|
346
|
-
]
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
console.log(img.pixels[0].color.rgb.r); // 255
|
|
350
|
-
console.log(img.pixels[1].color.value); // -16711936 (0xFF00FF00 as signed)
|
|
351
|
-
|
|
352
|
-
// Union nested in struct - direct property access!
|
|
353
|
-
img.pixels[0].color.rgb.g = 128; // Works correctly!
|
|
354
|
-
console.log(img.pixels[0].color.rgb.g); // 128
|
|
135
|
+
// cast() to POINTER — Python: cast(c_void_p(addr), POINTER(MyStruct))
|
|
136
|
+
const pData = cast(rawAddr, POINTER(MyStruct));
|
|
137
|
+
console.log(pData[0].field); // same result as fromAddress()
|
|
355
138
|
```
|
|
356
139
|
|
|
357
|
-
### Callbacks
|
|
140
|
+
### Callbacks
|
|
358
141
|
|
|
359
142
|
```javascript
|
|
360
|
-
import {
|
|
143
|
+
import { callback, c_int32, c_void_p, readValue } from 'node-ctypes';
|
|
361
144
|
|
|
362
|
-
const libc = new CDLL('msvcrt.dll'); // or libc.so.6 on Linux
|
|
363
|
-
|
|
364
|
-
// Create a comparison callback for qsort
|
|
365
145
|
const compare = callback(
|
|
366
|
-
(a, b) =>
|
|
367
|
-
|
|
368
|
-
const aVal = readValue(a, c_int32);
|
|
369
|
-
const bVal = readValue(b, c_int32);
|
|
370
|
-
return aVal - bVal;
|
|
371
|
-
},
|
|
372
|
-
c_int32, // return type
|
|
373
|
-
[c_void_p, c_void_p] // argument types: two pointers
|
|
146
|
+
(a, b) => readValue(a, c_int32) - readValue(b, c_int32),
|
|
147
|
+
c_int32, [c_void_p, c_void_p]
|
|
374
148
|
);
|
|
375
149
|
|
|
376
|
-
//
|
|
377
|
-
|
|
378
|
-
c_void_p, // array pointer
|
|
379
|
-
c_size_t, // number of elements
|
|
380
|
-
c_size_t, // element size
|
|
381
|
-
c_void_p // comparison function
|
|
382
|
-
]);
|
|
383
|
-
|
|
384
|
-
const arr = create_string_buffer(5 * 4);
|
|
385
|
-
const values = [5, 2, 8, 1, 9];
|
|
386
|
-
values.forEach((v, i) => writeValue(arr, c_int32, v, i * 4));
|
|
387
|
-
qsort(arr, 5, 4, compare.pointer);
|
|
388
|
-
|
|
389
|
-
// Array is now sorted: [1, 2, 5, 8, 9]
|
|
390
|
-
console.log(readValue(arr, c_int32, 0)); // 1
|
|
391
|
-
console.log(readValue(arr, c_int32, 4)); // 2
|
|
392
|
-
|
|
393
|
-
// IMPORTANT: Release callback when done
|
|
394
|
-
compare.release();
|
|
150
|
+
// Use with qsort, etc.
|
|
151
|
+
compare.release(); // Release when done
|
|
395
152
|
```
|
|
396
153
|
|
|
397
|
-
|
|
154
|
+
`CFUNCTYPE(restype, ...argtypes)` is also supported for Python-compatible function pointer types.
|
|
398
155
|
|
|
399
|
-
|
|
400
|
-
import { CDLL, create_string_buffer, string_at, c_int, c_void_p, c_char_p } from 'node-ctypes';
|
|
156
|
+
### Variadic functions
|
|
401
157
|
|
|
402
|
-
|
|
403
|
-
|
|
158
|
+
```javascript
|
|
159
|
+
import { CDLL, c_int, c_void_p, c_char_p, string_at } from 'node-ctypes';
|
|
404
160
|
|
|
405
|
-
|
|
161
|
+
const libc = new CDLL('libc.so.6'); // Linux
|
|
162
|
+
// const libc = new CDLL('msvcrt.dll'); // Windows
|
|
163
|
+
// const libc = new CDLL('libc.dylib'); // macOS
|
|
406
164
|
const sprintf = libc.func('sprintf', c_int, [c_void_p, c_char_p]);
|
|
407
165
|
|
|
408
|
-
const
|
|
409
|
-
|
|
410
|
-
//
|
|
411
|
-
sprintf(buffer, 'Hello %s!', 'World');
|
|
412
|
-
console.log(string_at(buffer)); // "Hello World!"
|
|
413
|
-
|
|
414
|
-
sprintf(buffer, 'Number: %d', 42);
|
|
415
|
-
console.log(string_at(buffer)); // "Number: 42"
|
|
416
|
-
|
|
417
|
-
sprintf(buffer, '%s: %d + %d = %d', 'Sum', 10, 20, 30);
|
|
418
|
-
console.log(string_at(buffer)); // "Sum: 10 + 20 = 30"
|
|
419
|
-
|
|
420
|
-
sprintf(buffer, 'Pi ≈ %.2f', 3.14159);
|
|
421
|
-
console.log(string_at(buffer)); // "Pi ≈ 3.14"
|
|
166
|
+
const buf = Buffer.alloc(256);
|
|
167
|
+
sprintf(buf, 'Hello %s! %d', 'World', 42); // Extra args auto-detected
|
|
168
|
+
console.log(string_at(buf)); // "Hello World! 42"
|
|
422
169
|
```
|
|
423
170
|
|
|
424
|
-
|
|
425
|
-
- ✅ Detects the extra arguments
|
|
426
|
-
- ✅ Infers their types (string → char*, number → int32/double, Buffer → pointer)
|
|
427
|
-
- ✅ Uses `ffi_prep_cif_var` for variadic call preparation
|
|
428
|
-
- ✅ Calls the function with correct argument marshalling
|
|
429
|
-
|
|
430
|
-
**This matches Python ctypes behavior exactly!**
|
|
431
|
-
|
|
432
|
-
### Windows API - Full Support
|
|
171
|
+
### Windows API
|
|
433
172
|
|
|
434
173
|
```javascript
|
|
435
|
-
import { WinDLL, Structure, c_uint16,
|
|
174
|
+
import { WinDLL, Structure, c_uint16, c_void, c_void_p } from 'node-ctypes';
|
|
436
175
|
|
|
437
|
-
|
|
438
|
-
const kernel32 = new WinDLL('kernel32.dll');
|
|
176
|
+
const kernel32 = new WinDLL('kernel32.dll'); // Uses __stdcall
|
|
439
177
|
|
|
440
|
-
// SYSTEMTIME structure (from tests/windows/test_winapi.js)
|
|
441
178
|
class SYSTEMTIME extends Structure {
|
|
442
179
|
static _fields_ = [
|
|
443
|
-
["wYear", c_uint16],
|
|
444
|
-
["
|
|
445
|
-
["
|
|
446
|
-
["
|
|
447
|
-
["wHour", c_uint16],
|
|
448
|
-
["wMinute", c_uint16],
|
|
449
|
-
["wSecond", c_uint16],
|
|
450
|
-
["wMilliseconds", c_uint16]
|
|
180
|
+
["wYear", c_uint16], ["wMonth", c_uint16],
|
|
181
|
+
["wDayOfWeek", c_uint16], ["wDay", c_uint16],
|
|
182
|
+
["wHour", c_uint16], ["wMinute", c_uint16],
|
|
183
|
+
["wSecond", c_uint16], ["wMilliseconds", c_uint16]
|
|
451
184
|
];
|
|
452
185
|
}
|
|
453
186
|
|
|
454
|
-
// Get local time
|
|
455
187
|
const GetLocalTime = kernel32.func('GetLocalTime', c_void, [c_void_p]);
|
|
456
|
-
|
|
457
188
|
const st = new SYSTEMTIME();
|
|
458
|
-
GetLocalTime(st);
|
|
459
|
-
|
|
189
|
+
GetLocalTime(st);
|
|
460
190
|
console.log(`${st.wYear}-${st.wMonth}-${st.wDay}`);
|
|
461
|
-
console.log(`${st.wHour}:${st.wMinute}:${st.wSecond}`);
|
|
462
|
-
|
|
463
|
-
// MessageBox (wide string version)
|
|
464
|
-
const user32 = new WinDLL('user32.dll');
|
|
465
|
-
const MessageBoxW = user32.func('MessageBoxW', c_int, [
|
|
466
|
-
c_void_p, // hWnd
|
|
467
|
-
c_wchar_p, // lpText
|
|
468
|
-
c_wchar_p, // lpCaption
|
|
469
|
-
c_uint32 // uType
|
|
470
|
-
]);
|
|
471
|
-
|
|
472
|
-
// Create UTF-16 buffers for wide strings
|
|
473
|
-
const text = Buffer.from('Hello from node-ctypes!\0', 'utf16le');
|
|
474
|
-
const caption = Buffer.from('node-ctypes\0', 'utf16le');
|
|
475
|
-
|
|
476
|
-
MessageBoxW(null, text, caption, 0);
|
|
477
191
|
```
|
|
478
192
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
```javascript
|
|
482
|
-
import { readValue, writeValue, sizeof, create_string_buffer, string_at, c_int, c_double, c_void_p } from 'node-ctypes';
|
|
483
|
-
|
|
484
|
-
// Allocate memory
|
|
485
|
-
const buf = Buffer.alloc(16);
|
|
486
|
-
|
|
487
|
-
// Write values at specific offsets
|
|
488
|
-
writeValue(buf, c_int, 12345, 0);
|
|
489
|
-
writeValue(buf, c_double, 3.14159, 8);
|
|
490
|
-
|
|
491
|
-
// Read values back
|
|
492
|
-
console.log(readValue(buf, c_int, 0)); // 12345
|
|
493
|
-
console.log(readValue(buf, c_double, 8)); // 3.14159
|
|
494
|
-
|
|
495
|
-
// Get type sizes
|
|
496
|
-
console.log(sizeof(c_int)); // 4
|
|
497
|
-
console.log(sizeof(c_double)); // 8
|
|
498
|
-
console.log(sizeof(c_void_p)); // 8 (on 64-bit)
|
|
499
|
-
|
|
500
|
-
// String handling
|
|
501
|
-
const str = create_string_buffer('Hello, World!');
|
|
502
|
-
console.log(string_at(str)); // "Hello, World!"
|
|
503
|
-
```
|
|
504
|
-
|
|
505
|
-
## Performance Benchmarks
|
|
506
|
-
|
|
507
|
-
Benchmarked on Windows with Node.js v24.11.0:
|
|
193
|
+
## API Compatibility Reference
|
|
508
194
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
- **Callback overhead**: koffi 1.5x faster at callback creation
|
|
527
|
-
|
|
528
|
-
### Struct Performance Tip
|
|
529
|
-
|
|
530
|
-
When reading struct fields repeatedly (e.g., in a tight loop), use `toObject()` to convert to a plain JavaScript object:
|
|
531
|
-
|
|
532
|
-
```javascript
|
|
533
|
-
const point = Point.create({ x: 10, y: 20 });
|
|
534
|
-
|
|
535
|
-
// ❌ Slow: Each access goes through Proxy → N-API → buffer read
|
|
536
|
-
for (let i = 0; i < 1000000; i++) {
|
|
537
|
-
const x = point.x; // ~80ns per access
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
// ✅ Fast: Convert once, then use plain object access
|
|
541
|
-
const obj = point.toObject(); // { x: 10, y: 20 }
|
|
542
|
-
for (let i = 0; i < 1000000; i++) {
|
|
543
|
-
const x = obj.x; // ~6ns per access (same as koffi!)
|
|
544
|
-
}
|
|
545
|
-
```
|
|
546
|
-
|
|
547
|
-
**When to use direct access vs toObject():**
|
|
548
|
-
- **Direct access (`point.x`)**: Always synchronized with underlying buffer; required when C code modifies the buffer
|
|
549
|
-
- **`toObject()`**: Snapshot copy; use for read-only loops or when passing data to JS-only code
|
|
550
|
-
|
|
551
|
-
**Transparent API overhead**: Only **3.5%** for auto `._buffer` extraction!
|
|
552
|
-
|
|
553
|
-
*See `tests/benchmarks/` for full benchmark suite.*
|
|
195
|
+
| Feature | Python ctypes | node-ctypes |
|
|
196
|
+
|---------|---------------|-------------|
|
|
197
|
+
| Load library | `CDLL("lib.so")` | `new CDLL("lib.so")` |
|
|
198
|
+
| Function setup | `f.argtypes = [c_int]` | `f.argtypes = [c_int]` |
|
|
199
|
+
| Structs | `class P(Structure):` | `class P extends Structure` |
|
|
200
|
+
| Unions | `class U(Union):` | `class U extends Union` |
|
|
201
|
+
| Arrays | `c_int * 5` | `array(c_int, 5)` |
|
|
202
|
+
| Bit fields | `("f", c_uint, 3)` | `["f", c_uint32, 3]` |
|
|
203
|
+
| Callbacks | `CFUNCTYPE(c_int, c_int)` | `CFUNCTYPE(c_int, c_int)` |
|
|
204
|
+
| Pointers | `POINTER(c_int)` / `pointer(obj)` | `POINTER(c_int)` / `pointer(obj)` |
|
|
205
|
+
| Sizeof | `sizeof(c_int)` | `sizeof(c_int)` |
|
|
206
|
+
| Alignment | `alignment(c_int)` | `alignment(c_int)` |
|
|
207
|
+
| Strings | `c_char_p(b"hello")` | `create_string_buffer("hello")` |
|
|
208
|
+
| Variadic | `sprintf(buf, b"%d", 42)` | `sprintf(buf, "%d", 42)` |
|
|
209
|
+
| Errno | `get_errno()` | `get_errno()` |
|
|
210
|
+
| byref | `byref(obj)` | `byref(obj)` |
|
|
211
|
+
| cast | `cast(ptr, type)` | `cast(ptr, type)` (supports `POINTER()` target) |
|
|
554
212
|
|
|
555
213
|
## Supported Types
|
|
556
214
|
|
|
557
|
-
| Type
|
|
558
|
-
|
|
559
|
-
| `
|
|
560
|
-
| `
|
|
561
|
-
| `
|
|
562
|
-
| `
|
|
563
|
-
| `
|
|
564
|
-
| `
|
|
565
|
-
| `
|
|
566
|
-
| `
|
|
567
|
-
| `
|
|
568
|
-
| `
|
|
569
|
-
| `
|
|
570
|
-
| `
|
|
571
|
-
| `
|
|
572
|
-
| `
|
|
573
|
-
| `
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
**Native classes and exports**
|
|
595
|
-
- `Version` - Version information exposed by the native module.
|
|
596
|
-
- `Library` - Represents a loaded native library and exposes low-level functions for symbols and library management.
|
|
597
|
-
- `FFIFunction` - Low-level object representing an FFI function (has properties like `address` and internal methods).
|
|
598
|
-
- `Callback` - Builds JS callbacks callable from C (main thread).
|
|
599
|
-
- `ThreadSafeCallback` - Builds thread-safe JS callbacks (can be called from external threads).
|
|
600
|
-
- `CType`, `StructType`, `ArrayType` - Types and helpers exposed by the native layer.
|
|
601
|
-
|
|
602
|
-
**Library loading and wrappers**
|
|
603
|
-
- `load(libPath)` → `Library` : loads a native library; `libPath` can be `null` for the current executable.
|
|
604
|
-
- `CDLL(libPath)` : common-use wrapper for C calls with cdecl convention; maintains a function cache and provides more convenient `func()`.
|
|
605
|
-
- `WinDLL(libPath)` : like `CDLL` but with `abi: 'stdcall'` by default (useful for WinAPI).
|
|
606
|
-
|
|
607
|
-
Example:
|
|
608
|
-
```js
|
|
609
|
-
import { CDLL, c_int32 } from './lib/index.js';
|
|
610
|
-
const libc = new CDLL(null);
|
|
611
|
-
|
|
612
|
-
// Traditional syntax
|
|
613
|
-
const abs = libc.func('abs', c_int32, [c_int32]);
|
|
614
|
-
console.log(abs(-5));
|
|
615
|
-
|
|
616
|
-
// Python ctypes-like syntax
|
|
617
|
-
const abs_func = libc.abs;
|
|
618
|
-
abs_func.argtypes = [c_int32];
|
|
619
|
-
abs_func.restype = c_int32;
|
|
620
|
-
console.log(abs_func(-5));
|
|
621
|
-
```
|
|
622
|
-
|
|
623
|
-
**Detailed CDLL API**
|
|
624
|
-
- `func(name, returnType, argTypes = [], options = {})` → `Function` : gets a callable function. The returned function is optimized and:
|
|
625
|
-
- automatically extracts `._buffer` from struct objects passed as arguments;
|
|
626
|
-
- exposes non-enumerable metadata: `funcName`, `address`, `_ffi`;
|
|
627
|
-
- provides the `errcheck` property as getter/setter to intercept return errors.
|
|
628
|
-
- **Python ctypes-like access**: `libc.functionName` returns a wrapper with `argtypes`/`restype`/`errcheck` properties for Python-compatible syntax.
|
|
629
|
-
- `symbol(name)` → `BigInt` : address of a symbol.
|
|
630
|
-
- `close()` : closes the library and clears the cache.
|
|
631
|
-
- `path` (getter) : library path.
|
|
632
|
-
- `loaded` (getter) : loading status.
|
|
633
|
-
|
|
634
|
-
**Callback**
|
|
635
|
-
- `callback(fn, returnType, argTypes = [])` → `{ pointer, release(), _callback }` : fast callback, main thread only.
|
|
636
|
-
- `threadSafeCallback(fn, returnType, argTypes = [])` → `{ pointer, release(), _callback }` : thread-safe callback for external threads.
|
|
637
|
-
|
|
638
|
-
Note: always call `release()` when a callback is no longer needed.
|
|
639
|
-
|
|
640
|
-
**Allocation and strings**
|
|
641
|
-
|
|
642
|
-
- `Buffer.alloc(size)` → `Buffer` : allocates native memory.
|
|
643
|
-
- `create_string_buffer(init)` → `Buffer` : creates null-terminated C string (init: size|string|Buffer).
|
|
644
|
-
- `create_unicode_buffer(init)` → `Buffer` : creates null-terminated wide string (wchar_t).
|
|
645
|
-
- `ptrToBuffer(address, size)` → `Buffer` : view on native address (use with caution).
|
|
646
|
-
- `addressof(ptr)` → `BigInt` : get the address as BigInt.
|
|
647
|
-
|
|
648
|
-
Example string creation and passing to function:
|
|
649
|
-
```js
|
|
650
|
-
import { create_string_buffer, CDLL, c_int32, c_void_p } from './lib/index.js';
|
|
651
|
-
const libc = new CDLL(null);
|
|
652
|
-
const puts = libc.func('puts', c_int32, [c_void_p]);
|
|
653
|
-
const s = create_string_buffer('hello');
|
|
654
|
-
puts(s);
|
|
655
|
-
```
|
|
656
|
-
|
|
657
|
-
**Reading and writing values**
|
|
658
|
-
- `readValue(ptr, type, offset = 0)` : supports fast-path for Buffer + basic types (`int8`, `uint8`, `int16`, `int32`, `int64`, `float`, `double`, `bool`).
|
|
659
|
-
- `writeValue(ptr, type, value, offset = 0)` : writes values with fast-path for Buffer.
|
|
660
|
-
|
|
661
|
-
**Types and helpers**
|
|
662
|
-
- `sizeof(type)` → `number` : size in bytes of a type.
|
|
663
|
-
- `POINTER(baseType)` : creates a pointer type factory (see [POINTER and pointer() API](#pointer-and-pointer-api) below).
|
|
664
|
-
- `pointer(obj)` : creates a pointer to an existing ctypes instance (Python-compatible).
|
|
665
|
-
- `byref(buffer)` : passes a buffer by reference (Python ctypes compatibility).
|
|
666
|
-
- `cast(ptr, targetType)` : interprets a pointer as another type (returns wrapper for struct).
|
|
667
|
-
|
|
668
|
-
**Struct / Union / Array / Bitfield**
|
|
669
|
-
- `struct(fields, options)` : defines struct with support for nested, bitfields, anonymous fields, packed option. Returns object with `create()`, `get()`, `set()`, `toObject()`, `getNestedBuffer()`.
|
|
670
|
-
- `union(fields)` : defines union; provides `create()`, `get()`, `set()`, `toObject()` and returns plain objects with properties.
|
|
671
|
-
- `array(elementType, count)` : defines ArrayType; `wrap(buffer)` returns Proxy with indexing.
|
|
672
|
-
- `bitfield(baseType, bits)` : bitfield definition.
|
|
673
|
-
|
|
674
|
-
Struct example:
|
|
675
|
-
```js
|
|
676
|
-
class Point extends Structure {
|
|
677
|
-
static _fields_ = [
|
|
678
|
-
["x", c_int32],
|
|
679
|
-
["y", c_int32]
|
|
680
|
-
];
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
const p = new Point(1, 2);
|
|
684
|
-
console.log(p.x, p.y); // 1 2
|
|
685
|
-
```
|
|
686
|
-
|
|
687
|
-
**Python-compatible conveniences**
|
|
688
|
-
- `create_string_buffer(init)` : create string buffer from number/string/Buffer.
|
|
689
|
-
- `create_unicode_buffer(init)` : create wide string buffer.
|
|
690
|
-
- `string_at(address, size)` / `wstring_at(address, size)` : read strings from address.
|
|
691
|
-
|
|
692
|
-
**Memory: utilities**
|
|
693
|
-
- `memmove(dst, src, count)` : copy memory.
|
|
694
|
-
- `memset(dst, value, count)` : set memory.
|
|
695
|
-
|
|
696
|
-
**Error handling and WinAPI helpers**
|
|
697
|
-
- `get_errno()` / `set_errno(value)` : access to errno (platform-specific implementation).
|
|
698
|
-
- `_initWinError()` internals; public helpers: `GetLastError()`, `SetLastError(code)`, `FormatError(code)`, `WinError(code)`.
|
|
699
|
-
|
|
700
|
-
**Type aliases (Python-compatible)**
|
|
701
|
-
The following type classes are exported (identical to Python ctypes):
|
|
702
|
-
`c_int, c_uint, c_int8, c_uint8, c_int16, c_uint16, c_int32, c_uint32, c_int64, c_uint64, c_float, c_double, c_char, c_char_p, c_wchar, c_wchar_p, c_void_p, c_bool, c_size_t, c_long, c_ulong`.
|
|
703
|
-
|
|
704
|
-
**Note**: Only type classes (e.g., `c_int32`) are supported, not string literals (e.g., `"int32"`). This matches Python ctypes behavior exactly.
|
|
705
|
-
|
|
706
|
-
**Constants**
|
|
707
|
-
- `POINTER_SIZE` - pointer size (from `native.POINTER_SIZE`).
|
|
708
|
-
- `WCHAR_SIZE` - wchar size (from `native.WCHAR_SIZE`).
|
|
709
|
-
- `NULL` - exported null value.
|
|
710
|
-
|
|
711
|
-
---
|
|
712
|
-
|
|
713
|
-
If you want, I can generate additional Windows or Linux-specific snippets, or integrate examples in the `tests/` directory.
|
|
714
|
-
|
|
715
|
-
### Functions
|
|
716
|
-
|
|
717
|
-
#### `load(libPath)` → `Library`
|
|
718
|
-
Load a shared library.
|
|
719
|
-
|
|
720
|
-
#### `callback(fn, returnType, argTypes)` → `{pointer, release()}`
|
|
721
|
-
Create a callback from a JavaScript function.
|
|
722
|
-
|
|
723
|
-
#### `create_string_buffer(init)` → `Buffer`
|
|
724
|
-
Create a null-terminated C string buffer (like Python ctypes). `init` can be a size, a string, or an existing `Buffer`.
|
|
725
|
-
|
|
726
|
-
#### `create_unicode_buffer(init)` → `Buffer`
|
|
727
|
-
Create a wide (wchar_t) null-terminated buffer (UTF-16LE on Windows).
|
|
728
|
-
|
|
729
|
-
#### `string_at(address, [size])` → `string`
|
|
730
|
-
Read a C string from an address or buffer.
|
|
731
|
-
|
|
732
|
-
#### `readValue(ptr, type, [offset])` → `value`
|
|
733
|
-
Read a value from memory.
|
|
734
|
-
|
|
735
|
-
#### `writeValue(ptr, type, value, [offset])` → `bytesWritten`
|
|
736
|
-
Write a value to memory.
|
|
737
|
-
|
|
738
|
-
#### `sizeof(type)` → `number`
|
|
739
|
-
Get the size of a type in bytes.
|
|
740
|
-
|
|
741
|
-
#### POINTER and pointer() API
|
|
742
|
-
|
|
743
|
-
`node-ctypes` provides a Python ctypes-compatible pointer API:
|
|
744
|
-
|
|
745
|
-
```javascript
|
|
746
|
-
import { POINTER, pointer, c_int32, Structure } from 'node-ctypes';
|
|
747
|
-
|
|
748
|
-
// Create a pointer type
|
|
749
|
-
const IntPtr = POINTER(c_int32);
|
|
750
|
-
|
|
751
|
-
// Create NULL pointer
|
|
752
|
-
const p1 = IntPtr.create();
|
|
753
|
-
console.log(p1.isNull); // true
|
|
754
|
-
|
|
755
|
-
// Create pointer from buffer
|
|
756
|
-
const buf = Buffer.alloc(12);
|
|
757
|
-
buf.writeInt32LE(10, 0);
|
|
758
|
-
buf.writeInt32LE(20, 4);
|
|
759
|
-
buf.writeInt32LE(30, 8);
|
|
215
|
+
| Type | Aliases | Size |
|
|
216
|
+
|------|---------|------|
|
|
217
|
+
| `c_int8` | `c_char` | 1 |
|
|
218
|
+
| `c_uint8` | `c_uchar` | 1 |
|
|
219
|
+
| `c_int16` | `c_short` | 2 |
|
|
220
|
+
| `c_uint16` | `c_ushort` | 2 |
|
|
221
|
+
| `c_int32` | `c_int` | 4 |
|
|
222
|
+
| `c_uint32` | `c_uint` | 4 |
|
|
223
|
+
| `c_int64` | `c_long` (64-bit) | 8 |
|
|
224
|
+
| `c_uint64` | `c_ulong` (64-bit) | 8 |
|
|
225
|
+
| `c_float` | | 4 |
|
|
226
|
+
| `c_double` | | 8 |
|
|
227
|
+
| `c_void_p` | pointer | 8 (64-bit) |
|
|
228
|
+
| `c_char_p` | string | pointer |
|
|
229
|
+
| `c_wchar_p` | wide string | pointer |
|
|
230
|
+
| `c_bool` | | 1 |
|
|
231
|
+
| `c_size_t` | | platform |
|
|
232
|
+
| `c_ssize_t` | | platform |
|
|
233
|
+
|
|
234
|
+
## Key Differences from Python ctypes
|
|
235
|
+
|
|
236
|
+
- Callbacks must be manually released with `.release()` to prevent memory leaks
|
|
237
|
+
- No automatic memory management for returned pointers
|
|
238
|
+
- Both `class extends Structure` (Python-like) and `struct({...})` (functional) syntaxes available
|
|
239
|
+
- Only type classes (`c_int32`) are accepted, not string literals (`"int32"`) — same as Python
|
|
240
|
+
|
|
241
|
+
## Utility Functions
|
|
242
|
+
|
|
243
|
+
- `create_string_buffer(init)` / `create_unicode_buffer(init)` — create C string buffers
|
|
244
|
+
- `string_at(address, size)` / `wstring_at(address, size)` — read strings from memory
|
|
245
|
+
- `readValue(ptr, type, offset)` / `writeValue(ptr, type, value, offset)` — direct memory access
|
|
246
|
+
- `sizeof(type)` — type size in bytes
|
|
247
|
+
- `alignment(type)` — type alignment in bytes
|
|
248
|
+
- `addressof(ptr)` — get address as BigInt
|
|
249
|
+
- `memmove(dst, src, count)` / `memset(dst, value, count)` — memory operations
|
|
250
|
+
- `GetLastError()` / `FormatError(code)` — Windows error helpers
|
|
760
251
|
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
// .contents property (Python-compatible)
|
|
764
|
-
console.log(p2.contents); // 10
|
|
765
|
-
p2.contents = 100;
|
|
766
|
-
console.log(buf.readInt32LE(0)); // 100
|
|
767
|
-
|
|
768
|
-
// Pointer indexing (Python-compatible)
|
|
769
|
-
console.log(p2[0]); // 100
|
|
770
|
-
console.log(p2[1]); // 20
|
|
771
|
-
console.log(p2[2]); // 30
|
|
772
|
-
|
|
773
|
-
p2[1] = 200;
|
|
774
|
-
console.log(buf.readInt32LE(4)); // 200
|
|
775
|
-
|
|
776
|
-
// pointer() function - create pointer to existing object
|
|
777
|
-
const x = new c_int32(42);
|
|
778
|
-
const px = pointer(x);
|
|
779
|
-
console.log(px.contents); // 42
|
|
780
|
-
px.contents = 100;
|
|
781
|
-
console.log(x.value); // 100
|
|
782
|
-
|
|
783
|
-
// Works with structures too
|
|
784
|
-
class Point extends Structure {
|
|
785
|
-
static _fields_ = [["x", c_int32], ["y", c_int32]];
|
|
786
|
-
}
|
|
787
|
-
const pt = new Point({ x: 10, y: 20 });
|
|
788
|
-
const ppt = pointer(pt);
|
|
789
|
-
```
|
|
252
|
+
## Examples
|
|
790
253
|
|
|
791
|
-
|
|
792
|
-
-
|
|
793
|
-
-
|
|
794
|
-
-
|
|
795
|
-
-
|
|
796
|
-
|
|
797
|
-
**Pointer instance properties:**
|
|
798
|
-
- `.contents` - get/set dereferenced value (Python-compatible)
|
|
799
|
-
- `.address` - get raw address as BigInt
|
|
800
|
-
- `.isNull` - check if pointer is NULL
|
|
801
|
-
- `[n]` - array-style indexing with pointer arithmetic
|
|
802
|
-
- `.deref()` - alias for `.contents` getter
|
|
803
|
-
- `.set(value)` - update pointer target
|
|
804
|
-
|
|
805
|
-
**Using POINTER in function definitions:**
|
|
806
|
-
```javascript
|
|
807
|
-
// POINTER types can be used as argtypes and restype
|
|
808
|
-
const IntPtr = POINTER(c_int32);
|
|
254
|
+
- [Windows Controls Demo](examples/windows/demo_controls.js) — Win32 common controls showcase
|
|
255
|
+
- [Windows Registry Demo](examples/windows/demo_registry.js) — setValue, getValue, openKey, deleteValue, deleteKey
|
|
256
|
+
- [Windows Tray Demo](examples/windows/demo_tray.js) — System tray menu
|
|
257
|
+
- [Windows COM Automation Demo](examples/windows/demo_comtypes.js) — IShellLinkW / IPersistFile COM interfaces
|
|
258
|
+
- [Windows LDAP Demo](examples/windows/demo_ldap.js) — LDAP directory queries
|
|
259
|
+
- [Smart Card (PC/SC) Demo](examples/demo_scard.js) — WinSCard on Windows, pcsclite on macOS/Linux
|
|
809
260
|
|
|
810
|
-
|
|
811
|
-
const memset = msvcrt.func("memset", c_void_p, [IntPtr, c_int32, c_size_t]);
|
|
261
|
+
## Tests
|
|
812
262
|
|
|
813
|
-
|
|
814
|
-
|
|
263
|
+
```bash
|
|
264
|
+
cd tests
|
|
265
|
+
npm install
|
|
266
|
+
npm run test
|
|
815
267
|
```
|
|
816
268
|
|
|
817
|
-
|
|
818
|
-
Create a simple struct definition.
|
|
269
|
+
See [tests/common/](tests/common/) for working examples including parallel Python implementations.
|
|
819
270
|
|
|
820
|
-
|
|
821
|
-
Base class for Python-like struct definitions. Subclasses should define `static _fields_`.
|
|
271
|
+
## Documentation
|
|
822
272
|
|
|
823
|
-
|
|
824
|
-
Base class for Python-like union definitions. Subclasses should define `static _fields_`.
|
|
273
|
+
Generate the API docs locally:
|
|
825
274
|
|
|
826
|
-
## Python ctypes Compatibility Reference
|
|
827
|
-
|
|
828
|
-
### API Comparison
|
|
829
|
-
|
|
830
|
-
| Feature | Python ctypes | node-ctypes |
|
|
831
|
-
|---------|---------------|-------------|
|
|
832
|
-
| **Load library** | `CDLL("lib.so")` | `new CDLL("lib.so")` |
|
|
833
|
-
| **Define function** | `lib.func.argtypes = [c_int]`<br>`lib.func.restype = c_int` | `lib.func("func", c_int, [c_int])`<br>**or**<br>`lib.func.argtypes = [c_int]`<br>`lib.func.restype = c_int` |
|
|
834
|
-
| **Structs** | `class Point(Structure):`<br> `_fields_ = [("x", c_int)]` | `class Point extends Structure`<br> `{ static _fields_ = [["x", c_int]] }` |
|
|
835
|
-
| **Unions** | `class U(Union):`<br> `_fields_ = [("i", c_int)]` | `class U extends Union`<br> `{ static _fields_ = [["i", c_int]] }` |
|
|
836
|
-
| **Arrays** | `c_int * 5` | `array(c_int, 5)` |
|
|
837
|
-
| **Bit fields** | `("flags", c_uint, 3)` | `["flags", c_uint32, 3]`<br>**or** `bitfield(c_uint32, 3)` |
|
|
838
|
-
| **Callbacks** | `CFUNCTYPE(c_int, c_int)` | `CFUNCTYPE(c_int, c_int)`<br>**or** ` callback(fn, c_int, [c_int])` |
|
|
839
|
-
| **Strings** | `c_char_p(b"hello")` | `create_string_buffer("hello")`<br>**or**<br>`new c_char_p(b"hello")` |
|
|
840
|
-
| **Pointers** | `POINTER(c_int)`<br>`p.contents`<br>`p[0]` | `POINTER(c_int)`<br>`p.contents`<br>`p[0]` |
|
|
841
|
-
| **pointer()** | `pointer(obj)` | `pointer(obj)` |
|
|
842
|
-
| **Variadic** | `sprintf(buf, b"%d", 42)` | `sprintf(buf, fmt, 42)` (auto) |
|
|
843
|
-
| **Sizeof** | `sizeof(c_int)` | `sizeof(c_int)` |
|
|
844
|
-
|
|
845
|
-
### What's Supported
|
|
846
|
-
|
|
847
|
-
✅ **Fully Compatible**:
|
|
848
|
-
- All basic types (int8-64, uint8-64, float, double, bool, pointer, string)
|
|
849
|
-
- Structs with nested structures
|
|
850
|
-
- Unions (including nested in structs)
|
|
851
|
-
- Bit fields
|
|
852
|
-
- Arrays (fixed-size)
|
|
853
|
-
- Callbacks (with manual release)
|
|
854
|
-
- Variadic functions (automatic detection)
|
|
855
|
-
- Anonymous fields in structs/unions
|
|
856
|
-
- **Class-based struct/union definitions** (`class MyStruct extends Structure`)
|
|
857
|
-
- Platform-specific types (c_long, c_ulong, c_size_t)
|
|
858
|
-
- Memory operations (alloc, read, write)
|
|
859
|
-
- Windows API (__stdcall via WinDLL)
|
|
860
|
-
|
|
861
|
-
⚠️ **Differences from Python ctypes**:
|
|
862
|
-
- Callbacks must be manually released with `.release()`
|
|
863
|
-
- **Function definition supports both syntaxes**: `func(name, returnType, argTypes)` **or** `func.argtypes = [...]; func.restype = ...`
|
|
864
|
-
|
|
865
|
-
✨ **100% Python-compatible**:
|
|
866
|
-
- **Struct property access**: Direct access (`p.x`, `p.y`) works with `class X extends Structure` - identical to Python!
|
|
867
|
-
- **Type system**: Only type classes (`c_int32`, `c_char_p`) are accepted, exactly like Python ctypes
|
|
868
|
-
- **No string literals**: `"int32"`, `"string"` are NOT supported (Python doesn't use them either)
|
|
869
|
-
|
|
870
|
-
## Limitations & Known Issues
|
|
871
|
-
|
|
872
|
-
- ⚠️ Callbacks must be released manually with `.release()` to prevent memory leaks
|
|
873
|
-
- ⚠️ No automatic memory management for returned pointers (manual `free()` required)
|
|
874
|
-
- ℹ️ Struct property access:
|
|
875
|
-
- **Python-style classes** (`class X extends Structure`): Direct property access (`p.x`, `p.y`) - fully compatible!
|
|
876
|
-
- **Plain struct definitions** (`struct({...})`): Use `.get()` / `.set()` methods for property access
|
|
877
|
-
- ℹ️ Struct alignment: Platform defaults are used, but `packed: true` option is available for packed structs
|
|
878
|
-
|
|
879
|
-
## Examples in Test Suite
|
|
880
|
-
|
|
881
|
-
For complete, working examples, see the test suite:
|
|
882
|
-
|
|
883
|
-
- **Basic types**: [tests/common/test_basic_types.js](tests/common/test_basic_types.js)
|
|
884
|
-
- **Structs & unions**: [tests/common/test_structs.js](tests/common/test_structs.js)
|
|
885
|
-
- **Nested structures**: [tests/common/test_nested_structs.js](tests/common/test_nested_structs.js)
|
|
886
|
-
- **Arrays**: [tests/common/test_arrays.js](tests/common/test_arrays.js)
|
|
887
|
-
- **Functions**: [tests/common/test_functions.js](tests/common/test_functions.js)
|
|
888
|
-
- **Callbacks**: [tests/common/test_callbacks.js](tests/common/test_callbacks.js)
|
|
889
|
-
- **Version info**: [tests/common/test_version.js](tests/common/test_version.js)
|
|
890
|
-
- **Windows API**: [tests/windows/test_winapi.js](tests/windows/test_winapi.js)
|
|
891
|
-
- **Python compatibility**: [tests/common/*.py](tests/common/) (parallel Python implementations)
|
|
892
|
-
|
|
893
|
-
Run tests:
|
|
894
275
|
```bash
|
|
895
|
-
|
|
896
|
-
npm install
|
|
897
|
-
npm run test # All tests
|
|
898
|
-
npm run bench:koffi # Benchmark vs koffi
|
|
276
|
+
npm run docs
|
|
899
277
|
```
|
|
900
278
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
For practical GUI application examples using the Windows API:
|
|
279
|
+
This produces a browsable site in `docs/` using [TypeDoc](https://typedoc.org/). To preview it:
|
|
904
280
|
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
- **WinScard - PC/SC Smart Card**: [examples/demo_scard.js](examples/demo_scard.js) - Comprehensive demo for Windows Smart Card API on Windows and PC/SC Lite (pcsclite) on macOS and Linux
|
|
281
|
+
```bash
|
|
282
|
+
npm run docs:serve
|
|
283
|
+
```
|
|
909
284
|
|
|
910
285
|
## License
|
|
911
286
|
|
|
@@ -913,7 +288,4 @@ MIT
|
|
|
913
288
|
|
|
914
289
|
## Credits
|
|
915
290
|
|
|
916
|
-
Built with
|
|
917
|
-
- [libffi](https://github.com/libffi/libffi) - Foreign Function Interface library
|
|
918
|
-
- [node-addon-api](https://github.com/nodejs/node-addon-api) - N-API C++ wrapper
|
|
919
|
-
- [cmake-js](https://github.com/cmake-js/cmake-js) - CMake-based build system for Node.js addons
|
|
291
|
+
Built with [libffi](https://github.com/libffi/libffi), [node-addon-api](https://github.com/nodejs/node-addon-api), and [cmake-js](https://github.com/cmake-js/cmake-js).
|