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 +5 -3
- 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/core/callback.js +8 -4
- package/lib/core/funcptr.js +188 -0
- package/lib/index.js +11 -4
- package/package.json +1 -1
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> `_fields_ = [("i", c_int)]` | `class U extends Union`<br> `{ 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
|
-
- **
|
|
906
|
-
- **Windows
|
|
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
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/lib/core/callback.js
CHANGED
|
@@ -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
|
|
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
|
|
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,
|