node-ctypes 1.4.0 → 1.5.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 CHANGED
@@ -835,7 +835,7 @@ Base class for Python-like union definitions. Subclasses should define `static _
835
835
  | **Unions** | `class U(Union):`<br>&nbsp;&nbsp;`_fields_ = [("i", c_int)]` | `class U extends Union`<br>&nbsp;&nbsp;`{ static _fields_ = [["i", c_int]] }` |
836
836
  | **Arrays** | `c_int * 5` | `array(c_int, 5)` |
837
837
  | **Bit fields** | `("flags", c_uint, 3)` | `["flags", c_uint32, 3]`<br>**or** `bitfield(c_uint32, 3)` |
838
- | **Callbacks** | `CFUNCTYPE(c_int, c_int)` | `callback(fn, c_int, [c_int])` |
838
+ | **Callbacks** | `CFUNCTYPE(c_int, c_int)` | `CFUNCTYPE(c_int, c_int)`<br>**or** ` callback(fn, c_int, [c_int])` |
839
839
  | **Strings** | `c_char_p(b"hello")` | `create_string_buffer("hello")`<br>**or**<br>`new c_char_p(b"hello")` |
840
840
  | **Pointers** | `POINTER(c_int)`<br>`p.contents`<br>`p[0]` | `POINTER(c_int)`<br>`p.contents`<br>`p[0]` |
841
841
  | **pointer()** | `pointer(obj)` | `pointer(obj)` |
@@ -902,8 +902,10 @@ npm run bench:koffi # Benchmark vs koffi
902
902
 
903
903
  For practical GUI application examples using the Windows API:
904
904
 
905
- - **Simple GUI Demo**: [examples/windows/simple.js](examples/windows/simple.js) - Message boxes and basic Windows API GUI elements
906
- - **Windows Controls Showcase Demo**: [examples/windows/windows_controls.js](examples/windows/windows_controls.js) - Comprehensive demo with a wide set of common Win32 controls
905
+ - **Windows Controls Showcase Demo**: [examples/windows/demo_controls.js](examples/windows/demo_controls.js) - Comprehensive demo with a wide set of common Win32 controls
906
+ - **Windows Registry Demo**: [examples/windows/demo_registry](examples/windows/demo_registry) - Comprehensive demo with setValue, getValue, openKey, deleteValue, deleteKey
907
+ - **Windows Tray Demo**: [examples/windows/demo_tray.js](examples/windows/demo_tray.js) - Comprehensive demo for tray menu inspired by [pit-ray/fluent-tray](https://github.com/pit-ray/fluent-tray)
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
907
909
 
908
910
  ## License
909
911
 
Binary file
Binary file
@@ -150,8 +150,10 @@ import { _toNativeType, _toNativeTypes } from "./types.js";
150
150
  * @throws {Error} If callback creation fails or type conversion fails
151
151
  * @see {@link threadSafeCallback} for thread-safe callbacks
152
152
  */
153
- export function callback(fn, returnType, argTypes = [], native) {
154
- const cb = new native.Callback(fn, _toNativeType(returnType, native), _toNativeTypes(argTypes, native));
153
+ export function callback(fn, returnType, argTypes = [], native, abi = undefined) {
154
+ const args = [fn, _toNativeType(returnType, native), _toNativeTypes(argTypes, native)];
155
+ if (abi) args.push(abi);
156
+ const cb = new native.Callback(...args);
155
157
 
156
158
  return {
157
159
  get pointer() {
@@ -260,8 +262,10 @@ export function callback(fn, returnType, argTypes = [], native) {
260
262
  * @throws {Error} If callback creation fails or type conversion fails
261
263
  * @see {@link callback} for main-thread-only callbacks
262
264
  */
263
- export function threadSafeCallback(fn, returnType, argTypes = [], native) {
264
- const cb = new native.ThreadSafeCallback(fn, _toNativeType(returnType, native), _toNativeTypes(argTypes, native));
265
+ export function threadSafeCallback(fn, returnType, argTypes = [], native, abi = undefined) {
266
+ const args = [fn, _toNativeType(returnType, native), _toNativeTypes(argTypes, native)];
267
+ if (abi) args.push(abi);
268
+ const cb = new native.ThreadSafeCallback(...args);
265
269
 
266
270
  return {
267
271
  get pointer() {
@@ -0,0 +1,188 @@
1
+ /**
2
+ * @file funcptr.js
3
+ * @module core/funcptr
4
+ * @description Function pointer type factories for calling raw function pointers.
5
+ *
6
+ * Provides CFUNCTYPE and WINFUNCTYPE factories that create callable function
7
+ * types from raw memory addresses (BigInt). This enables COM vtable dispatch
8
+ * and other scenarios where function pointers are obtained at runtime.
9
+ *
10
+ * **Python ctypes Compatibility**:
11
+ * - `CFUNCTYPE(restype, ...argtypes)` ≈ Python's `ctypes.CFUNCTYPE`
12
+ * - `WINFUNCTYPE(restype, ...argtypes)` ≈ Python's `ctypes.WINFUNCTYPE`
13
+ *
14
+ * @example Calling a function by address
15
+ * ```javascript
16
+ * import { WinDLL, WINFUNCTYPE, c_int32, c_void_p, c_wchar_p, c_uint32 } from 'node-ctypes';
17
+ *
18
+ * const user32 = new WinDLL('user32.dll');
19
+ * const msgBoxAddr = user32.symbol('MessageBoxW');
20
+ *
21
+ * const MsgBoxProto = WINFUNCTYPE(c_int32, c_void_p, c_wchar_p, c_wchar_p, c_uint32);
22
+ * const msgBox = MsgBoxProto(msgBoxAddr);
23
+ * msgBox(null, 'Hello from FuncPtr!', 'Test', 0);
24
+ * ```
25
+ *
26
+ * @example COM vtable dispatch
27
+ * ```javascript
28
+ * import { WINFUNCTYPE, c_int32, c_void_p, ptrToBuffer } from 'node-ctypes';
29
+ *
30
+ * // Read vtable pointer from COM object
31
+ * const vtableBuf = ptrToBuffer(objPtr, 8);
32
+ * const vtableAddr = vtableBuf.readBigUInt64LE(0);
33
+ * const vtable = ptrToBuffer(vtableAddr, 24); // 3 IUnknown methods * 8 bytes
34
+ *
35
+ * // QueryInterface is the first vtable entry
36
+ * const QueryInterface = WINFUNCTYPE(c_int32, c_void_p, c_void_p, c_void_p);
37
+ * const qi = QueryInterface(vtable.readBigUInt64LE(0));
38
+ * ```
39
+ */
40
+
41
+ import { _toNativeType, _toNativeTypes } from "./types.js";
42
+
43
+ /**
44
+ * Creates a function pointer type factory with cdecl calling convention.
45
+ *
46
+ * @param {Function|Object|number} restype - Return type
47
+ * @param {...(Function|Object|number)} argtypes - Argument types
48
+ * @returns {Function} Factory that accepts a BigInt address and returns a callable
49
+ */
50
+ export function createCFUNCTYPE(native) {
51
+ return function CFUNCTYPE(restype, ...argtypes) {
52
+ const nativeRestype = _toNativeType(restype, native);
53
+ const nativeArgtypes = _toNativeTypes(argtypes, native);
54
+
55
+ function createFromAddress(address) {
56
+ if (typeof address !== "bigint") {
57
+ throw new TypeError("CFUNCTYPE: address must be a BigInt");
58
+ }
59
+
60
+ const ffiFunc = new native.FFIFunction(address, null, nativeRestype, nativeArgtypes);
61
+
62
+ const callMethod = function (...args) {
63
+ const processedArgs = processArgs(args);
64
+ return ffiFunc.call(...processedArgs);
65
+ };
66
+
67
+ Object.defineProperties(callMethod, {
68
+ funcName: { value: `0x${address.toString(16)}` },
69
+ address: { value: ffiFunc.address },
70
+ _ffi: { value: ffiFunc },
71
+ callAsync: {
72
+ value: function (...args) {
73
+ const processedArgs = processArgs(args);
74
+ return ffiFunc.callAsync(...processedArgs);
75
+ },
76
+ writable: false,
77
+ enumerable: false,
78
+ configurable: false,
79
+ },
80
+ errcheck: {
81
+ get() {
82
+ return ffiFunc._errcheck;
83
+ },
84
+ set(callback) {
85
+ ffiFunc._errcheck = callback;
86
+ ffiFunc.setErrcheck(callback);
87
+ },
88
+ enumerable: false,
89
+ configurable: true,
90
+ },
91
+ });
92
+
93
+ return callMethod;
94
+ }
95
+
96
+ // Attach metadata to the factory
97
+ createFromAddress.restype = restype;
98
+ createFromAddress.argtypes = argtypes;
99
+ createFromAddress._isFuncPtr = true;
100
+
101
+ return createFromAddress;
102
+ };
103
+ }
104
+
105
+ /**
106
+ * Creates a function pointer type factory with stdcall calling convention.
107
+ *
108
+ * @param {Function|Object|number} restype - Return type
109
+ * @param {...(Function|Object|number)} argtypes - Argument types
110
+ * @returns {Function} Factory that accepts a BigInt address and returns a callable
111
+ */
112
+ export function createWINFUNCTYPE(native) {
113
+ return function WINFUNCTYPE(restype, ...argtypes) {
114
+ const nativeRestype = _toNativeType(restype, native);
115
+ const nativeArgtypes = _toNativeTypes(argtypes, native);
116
+
117
+ function createFromAddress(address) {
118
+ if (typeof address !== "bigint") {
119
+ throw new TypeError("WINFUNCTYPE: address must be a BigInt");
120
+ }
121
+
122
+ const ffiFunc = new native.FFIFunction(address, null, nativeRestype, nativeArgtypes, { abi: "stdcall" });
123
+
124
+ const callMethod = function (...args) {
125
+ const processedArgs = processArgs(args);
126
+ return ffiFunc.call(...processedArgs);
127
+ };
128
+
129
+ Object.defineProperties(callMethod, {
130
+ funcName: { value: `0x${address.toString(16)}` },
131
+ address: { value: ffiFunc.address },
132
+ _ffi: { value: ffiFunc },
133
+ callAsync: {
134
+ value: function (...args) {
135
+ const processedArgs = processArgs(args);
136
+ return ffiFunc.callAsync(...processedArgs);
137
+ },
138
+ writable: false,
139
+ enumerable: false,
140
+ configurable: false,
141
+ },
142
+ errcheck: {
143
+ get() {
144
+ return ffiFunc._errcheck;
145
+ },
146
+ set(callback) {
147
+ ffiFunc._errcheck = callback;
148
+ ffiFunc.setErrcheck(callback);
149
+ },
150
+ enumerable: false,
151
+ configurable: true,
152
+ },
153
+ });
154
+
155
+ return callMethod;
156
+ }
157
+
158
+ // Attach metadata to the factory
159
+ createFromAddress.restype = restype;
160
+ createFromAddress.argtypes = argtypes;
161
+ createFromAddress._isFuncPtr = true;
162
+
163
+ return createFromAddress;
164
+ };
165
+ }
166
+
167
+ /**
168
+ * Process arguments, extracting _buffer from struct/union proxies.
169
+ * @private
170
+ */
171
+ function processArgs(args) {
172
+ const processedArgs = [];
173
+ for (let i = 0; i < args.length; i++) {
174
+ const arg = args[i];
175
+ if (arg && typeof arg === "object") {
176
+ if (arg._buffer !== undefined && Buffer.isBuffer(arg._buffer)) {
177
+ processedArgs.push(arg._buffer);
178
+ } else if (Buffer.isBuffer(arg)) {
179
+ processedArgs.push(arg);
180
+ } else {
181
+ processedArgs.push(arg);
182
+ }
183
+ } else {
184
+ processedArgs.push(arg);
185
+ }
186
+ }
187
+ return processedArgs;
188
+ }
package/lib/index.js CHANGED
@@ -23,6 +23,7 @@ import { LRUCache } from "./utils/cache.js";
23
23
  import { _initConstants } from "./platform/constants.js";
24
24
  import { _toNativeType, _toNativeTypes } from "./core/types.js";
25
25
  import { callback as createCallback, threadSafeCallback as createThreadSafeCallback } from "./core/callback.js";
26
+ import { createCFUNCTYPE, createWINFUNCTYPE } from "./core/funcptr.js";
26
27
  import { createLibraryClasses } from "./core/Library.js";
27
28
  import {
28
29
  alloc as _alloc,
@@ -138,6 +139,10 @@ function load(libPath) {
138
139
  // Create Library classes (FunctionWrapper, CDLL, WinDLL)
139
140
  const { FunctionWrapper, CDLL, WinDLL } = createLibraryClasses(Library, LRUCache, _toNativeType, _toNativeTypes, native);
140
141
 
142
+ // Function pointer type factories (Python ctypes CFUNCTYPE/WINFUNCTYPE)
143
+ const CFUNCTYPE = createCFUNCTYPE(native);
144
+ const WINFUNCTYPE = createWINFUNCTYPE(native);
145
+
141
146
  // ============================================================================
142
147
  // Callback Functions
143
148
  // Now imported from ./core/callback.js - see that file for implementation details
@@ -147,16 +152,16 @@ const { FunctionWrapper, CDLL, WinDLL } = createLibraryClasses(Library, LRUCache
147
152
  * Crea un callback JS chiamabile da C (solo main thread)
148
153
  * @see ./core/callback.js for full documentation
149
154
  */
150
- function callback(fn, returnType, argTypes = []) {
151
- return createCallback(fn, returnType, argTypes, native);
155
+ function callback(fn, returnType, argTypes = [], abi = undefined) {
156
+ return createCallback(fn, returnType, argTypes, native, abi);
152
157
  }
153
158
 
154
159
  /**
155
160
  * Crea un callback JS chiamabile da qualsiasi thread
156
161
  * @see ./core/callback.js for full documentation
157
162
  */
158
- function threadSafeCallback(fn, returnType, argTypes = []) {
159
- return createThreadSafeCallback(fn, returnType, argTypes, native);
163
+ function threadSafeCallback(fn, returnType, argTypes = [], abi = undefined) {
164
+ return createThreadSafeCallback(fn, returnType, argTypes, native, abi);
160
165
  }
161
166
 
162
167
  // ============================================================================
@@ -484,6 +489,8 @@ export {
484
489
  load,
485
490
  callback,
486
491
  threadSafeCallback,
492
+ CFUNCTYPE,
493
+ WINFUNCTYPE,
487
494
 
488
495
  // Memory Management - Python-compatibili
489
496
  create_string_buffer,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-ctypes",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Python ctypes-like FFI for Node.js using libffi",
5
5
  "author": "Damiano Mazzella",
6
6
  "license": "MIT",