koffi 3.0.1 → 3.1.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/CHANGELOG.md +32 -3
- package/cnoke.cjs +2 -2
- package/doc/benchmarks.md +1 -1
- package/doc/callbacks.md +7 -26
- package/doc/{input.md → composites.md} +161 -147
- package/doc/contribute.md +3 -2
- package/doc/index.md +0 -14
- package/doc/{functions.md → load.md} +54 -113
- package/doc/migration.md +4 -7
- package/doc/misc.md +0 -103
- package/doc/output.md +5 -11
- package/doc/pointers.md +76 -17
- package/doc/primitives.md +151 -0
- package/doc/start.md +3 -13
- package/doc/types.md +88 -0
- package/doc/unions.md +0 -186
- package/doc/values.md +134 -0
- package/index.d.ts +375 -308
- package/lib/native/base/base.cc +66 -24
- package/lib/native/base/base.hh +55 -153
- package/package.json +16 -16
- package/src/koffi/CMakeLists.txt +20 -17
- package/src/koffi/index.cjs +30 -111
- package/src/koffi/index.js +22 -96
- package/src/koffi/indirect.cjs +30 -111
- package/src/koffi/indirect.js +24 -24
- package/src/koffi/src/abi/arm64.cc +48 -62
- package/src/koffi/src/abi/riscv64.cc +39 -57
- package/src/koffi/src/abi/x64sysv.cc +39 -57
- package/src/koffi/src/abi/x64win.cc +48 -65
- package/src/koffi/src/abi/x86.cc +47 -59
- package/src/koffi/src/call.cc +426 -209
- package/src/koffi/src/call.hh +7 -11
- package/src/koffi/src/ffi.cc +534 -303
- package/src/koffi/src/ffi.hh +71 -15
- package/src/koffi/src/parser.cc +5 -3
- package/src/koffi/src/parser.hh +2 -2
- package/src/koffi/src/static.cjs +122 -0
- package/src/koffi/src/static.js +125 -0
- package/src/koffi/src/type.cc +725 -0
- package/src/koffi/src/type.hh +71 -0
- package/src/koffi/src/util.cc +117 -1202
- package/src/koffi/src/util.hh +158 -156
- package/src/koffi/src/uv.cc +17 -11
- package/src/koffi/src/uv.hh +2 -1
- package/vendor/node-addon-api/README.md +1 -1
- package/vendor/node-addon-api/napi-inl.h +213 -35
- package/vendor/node-addon-api/napi.h +118 -7
- package/doc/variables.md +0 -102
- package/indirect.d.ts +0 -322
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Number types
|
|
2
|
+
|
|
3
|
+
While the C standard allows for variation in the size of most integer types, Koffi enforces the same definition for most primitive types, listed below:
|
|
4
|
+
|
|
5
|
+
C type | JS type | Bytes | Signedness | Note
|
|
6
|
+
----------------------------- | ---------------- | ----- | ---------- | ---------------------------
|
|
7
|
+
void | Undefined | 0 | | Only valid as a return type
|
|
8
|
+
int8, int8_t | Number (integer) | 1 | Signed |
|
|
9
|
+
uint8, uint8_t | Number (integer) | 1 | Unsigned |
|
|
10
|
+
char | Number (integer) | 1 | Signed |
|
|
11
|
+
uchar, unsigned char | Number (integer) | 1 | Unsigned |
|
|
12
|
+
char16, char16_t | Number (integer) | 2 | Signed |
|
|
13
|
+
int16, int16_t | Number (integer) | 2 | Signed |
|
|
14
|
+
uint16, uint16_t | Number (integer) | 2 | Unsigned |
|
|
15
|
+
short | Number (integer) | 2 | Signed |
|
|
16
|
+
ushort, unsigned short | Number (integer) | 2 | Unsigned |
|
|
17
|
+
char32, char32_t | Number (integer) | 4 | Signed |
|
|
18
|
+
int32, int32_t | Number (integer) | 4 | Signed |
|
|
19
|
+
uint32, uint32_t | Number (integer) | 4 | Unsigned |
|
|
20
|
+
int | Number (integer) | 4 | Signed |
|
|
21
|
+
uint, unsigned int | Number (integer) | 4 | Unsigned |
|
|
22
|
+
int64, int64_t | Number (integer) | 8 | Signed |
|
|
23
|
+
uint64, uint64_t | Number (integer) | 8 | Unsigned |
|
|
24
|
+
longlong, long long | Number (integer) | 8 | Signed |
|
|
25
|
+
ulonglong, unsigned long long | Number (integer) | 8 | Unsigned |
|
|
26
|
+
float32 | Number (float) | 4 | |
|
|
27
|
+
float64 | Number (float) | 8 | |
|
|
28
|
+
float | Number (float) | 4 | |
|
|
29
|
+
double | Number (float) | 8 | |
|
|
30
|
+
|
|
31
|
+
Koffi also accepts BigInt values when converting from JS to C integers. If the value exceeds the range of the C type, Koffi will convert the number to an undefined value. In the reverse direction, BigInt values are automatically used when needed for big 64-bit integers.
|
|
32
|
+
|
|
33
|
+
Koffi defines a few more types that can change size depending on the OS and the architecture:
|
|
34
|
+
|
|
35
|
+
C type | JS type | Signedness | Note
|
|
36
|
+
---------------- | ---------------- | ----------- | ------------------------------------------------
|
|
37
|
+
bool | Boolean | | Usually one byte
|
|
38
|
+
long | Number (integer) | Signed | 4 or 8 bytes depending on platform (LP64, LLP64)
|
|
39
|
+
ulong | Number (integer) | Unsigned | 4 or 8 bytes depending on platform (LP64, LLP64)
|
|
40
|
+
unsigned long | Number (integer) | Unsigned | 4 or 8 bytes depending on platform (LP64, LLP64)
|
|
41
|
+
intptr | Number (integer) | Signed | 4 or 8 bytes depending on register width
|
|
42
|
+
intptr_t | Number (integer) | Signed | 4 or 8 bytes depending on register width
|
|
43
|
+
uintptr | Number (integer) | Unsigned | 4 or 8 bytes depending on register width
|
|
44
|
+
uintptr_t | Number (integer) | Unsigned | 4 or 8 bytes depending on register width
|
|
45
|
+
wchar_t | Number (integer) | | 2 bytes on Windows, 4 bytes Linux, macOS and BSD
|
|
46
|
+
|
|
47
|
+
Primitive types can be specified by name (in a string) or through `koffi.types`:
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
// These two lines do the same:
|
|
51
|
+
let struct1 = koffi.struct({ dummy: 'long' });
|
|
52
|
+
let struct2 = koffi.struct({ dummy: koffi.types.long });
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
# String types
|
|
56
|
+
|
|
57
|
+
Koffi can convert JS strings to and from UTF-8, UTF-16 or UTF-32.
|
|
58
|
+
|
|
59
|
+
Type name | Aliases | JS type | Conversion / encoding
|
|
60
|
+
------------------------------ | --------------- | ------- | ---------------------
|
|
61
|
+
const char \*, char \* | str, string | String | UTF-8
|
|
62
|
+
const char16_t \*, char16_t \* | str16, string16 | String | UTF-16
|
|
63
|
+
const char32_t \*, char32_t \* | str32, string32 | String | UTF-32
|
|
64
|
+
|
|
65
|
+
Koffi also supports wide strings (`wchar_t *` or `wstring`), which use:
|
|
66
|
+
|
|
67
|
+
- UTF-16 on Windows (where wchar_t is 2 bytes)
|
|
68
|
+
- UTF-32 on other platforms (where wchar_t is 4 bytes)
|
|
69
|
+
|
|
70
|
+
# Enum types
|
|
71
|
+
|
|
72
|
+
*New in Koffi 3.0*
|
|
73
|
+
|
|
74
|
+
C enumeration values are stored as integers. The underlying integer type is implementation-defined but Koffi tries to match usual platform behavior.
|
|
75
|
+
|
|
76
|
+
On POSIX platforms, Koffi follows the following rules:
|
|
77
|
+
|
|
78
|
+
- If no negative value exists: `unsigned int` by default, `uint64_t` if needed
|
|
79
|
+
- If any negative value exists: `int` by default, `int64_t` if needed
|
|
80
|
+
|
|
81
|
+
On Windows, things are simpler, and `int` is used all the time. Koffi will throw an error if any enumeration value does not fit in a 32-bit integer.
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
// For OpenResult, the underlying type will be unsigned int on POSIX, int on Windows
|
|
85
|
+
const OpenResult = koffi.enumeration('OpenResult', {
|
|
86
|
+
Success: 0,
|
|
87
|
+
MissingFile: 1,
|
|
88
|
+
AccessDenied: 2,
|
|
89
|
+
OtherError: 3
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// For RelativePosition, the underlying type will be int everywhere
|
|
93
|
+
const RelativePosition = koffi.enumeration('RelativePosition', {
|
|
94
|
+
Left: -1,
|
|
95
|
+
Center: 0,
|
|
96
|
+
Right: 1
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// For IntLimits, the underlying type will be int64_t on POSIX, and fail on Windows
|
|
100
|
+
const Int64Limits = koffi.enumeration('Int64Limits', {
|
|
101
|
+
Min: -9223372036854775808n,
|
|
102
|
+
Max: 9223372036854775807n
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
> [!WARNING]
|
|
107
|
+
> This behavior may not match your compiler:
|
|
108
|
+
>
|
|
109
|
+
> - On POSIX platforms, GCC and Clang will use a short integer type if `-fshort-enums` is specified and the enumeration values fit in `short` or `unsigned short`.
|
|
110
|
+
> - On Windows, MSVC (and Clang) always use `int` even if some values do not fit, which matches what Koffi does... unless the compiler flag `/Zc:enumTypes` is set, maybe.
|
|
111
|
+
>
|
|
112
|
+
> Use an explicit type specifier to work around these problems, as shown below.
|
|
113
|
+
|
|
114
|
+
You can access the constants in `values` member of the type object.
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
console.log(OpenResult.values.MissingFile); // Prints 1
|
|
118
|
+
console.log(RelativePosition.values.Left); // Prints -1
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
You can specify the storage type explicitly as the last argument: `koffi.enumeration(name, values, type)`.
|
|
122
|
+
|
|
123
|
+
```js
|
|
124
|
+
// This one explictly uses int64_t as the underlying type, despite the fact that the values fit inside an int.
|
|
125
|
+
const ExplicitEnum = koffi.enumeration('ExplicitEnum', {
|
|
126
|
+
Zero: 0,
|
|
127
|
+
One: 1,
|
|
128
|
+
Two: 2
|
|
129
|
+
}, 'int64_t');
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
# Endian-sensitive integers
|
|
133
|
+
|
|
134
|
+
Koffi defines a bunch of endian-sensitive types, which can be used when dealing with binary data (network payloads, binary file formats, etc.).
|
|
135
|
+
|
|
136
|
+
C type | Bytes | Signedness | Endianness
|
|
137
|
+
---------------------- | ----- | ---------- | -------------
|
|
138
|
+
int16_le, int16_le_t | 2 | Signed | Little Endian
|
|
139
|
+
int16_be, int16_be_t | 2 | Signed | Big Endian
|
|
140
|
+
uint16_le, uint16_le_t | 2 | Unsigned | Little Endian
|
|
141
|
+
uint16_be, uint16_be_t | 2 | Unsigned | Big Endian
|
|
142
|
+
int32_le, int32_le_t | 4 | Signed | Little Endian
|
|
143
|
+
int32_be, int32_be_t | 4 | Signed | Big Endian
|
|
144
|
+
uint32_le, uint32_le_t | 4 | Unsigned | Little Endian
|
|
145
|
+
uint32_be, uint32_be_t | 4 | Unsigned | Big Endian
|
|
146
|
+
int64_le, int64_le_t | 8 | Signed | Little Endian
|
|
147
|
+
int64_be, int64_be_t | 8 | Signed | Big Endian
|
|
148
|
+
uint64_le, uint64_le_t | 8 | Unsigned | Little Endian
|
|
149
|
+
uint64_be, uint64_be_t | 8 | Unsigned | Big Endian
|
|
150
|
+
|
|
151
|
+
You can find an example that uses these types to [extract information from a PNG header](pointers#handling-void-pointers) with transparent endian conversion.
|
package/doc/start.md
CHANGED
|
@@ -16,6 +16,9 @@ import koffi from 'koffi';
|
|
|
16
16
|
const koffi = require('koffi');
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
+
> [!NOTE]
|
|
20
|
+
> Follow the [build instructions](contribute#build-from-source) if you want to build the native Koffi code yourself, to fix something or simply to study the code.
|
|
21
|
+
|
|
19
22
|
# Simple examples
|
|
20
23
|
|
|
21
24
|
Below you can find two examples:
|
|
@@ -105,16 +108,3 @@ let ret = MessageBoxA(null, 'Do you want another message box?', 'Koffi', MB_YESN
|
|
|
105
108
|
if (ret == IDYES)
|
|
106
109
|
MessageBoxW(null, 'Hello World!', 'Koffi', MB_ICONINFORMATION);
|
|
107
110
|
```
|
|
108
|
-
|
|
109
|
-
# Bundling Koffi
|
|
110
|
-
|
|
111
|
-
Please read the [dedicated page](packaging) for information about bundling and packaging applications using Koffi.
|
|
112
|
-
|
|
113
|
-
# Build manually
|
|
114
|
-
|
|
115
|
-
Follow the [build instructions](contribute#build-from-source) if you want to build the native Koffi code yourself.
|
|
116
|
-
|
|
117
|
-
> [!NOTE]
|
|
118
|
-
> This is only needed if you want to hack on Koffi. The official NPM package provides prebuilt binaries and you don't need to compile anything if you only want to use Koffi in Node.js.
|
|
119
|
-
>
|
|
120
|
-
> Just run `npm install koffi`!
|
package/doc/types.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Type specifiers
|
|
2
|
+
|
|
3
|
+
*Changed in Koffi 3.0*
|
|
4
|
+
|
|
5
|
+
> [!NOTE]
|
|
6
|
+
> In Koffi 2.0, types were External values, you had to use `koffi.introspect()` to get type information. In Koffi 3.0, this information is directly available in type objects, and this function is deprecated.
|
|
7
|
+
>
|
|
8
|
+
> Consult the [migration guide](migration) for more information.
|
|
9
|
+
|
|
10
|
+
You can use strings or type objects to give type information to Koffi (when declaring functions, structs, and so on). Use `koffi.type(spec)` to resolve all accepted type values (strings and type objects) to type objects.
|
|
11
|
+
|
|
12
|
+
You can inspect the type object for information: name, primitive, size, alignment, members (record types), reference type (array, pointer), length (array), arguments and return type (prototypes).
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
const FoobarType = koffi.struct('FoobarType', {
|
|
16
|
+
a: 'int',
|
|
17
|
+
b: 'char *',
|
|
18
|
+
c: 'double'
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
console.log(FoobarType);
|
|
22
|
+
|
|
23
|
+
// Expected result on 64-bit machines:
|
|
24
|
+
// {
|
|
25
|
+
// name: 'FoobarType',
|
|
26
|
+
// primitive: 'Record',
|
|
27
|
+
// size: 24,
|
|
28
|
+
// alignment: 8,
|
|
29
|
+
// disposable: false,
|
|
30
|
+
// members: {
|
|
31
|
+
// a: { name: 'a', type: [Type], offset: 0 },
|
|
32
|
+
// b: { name: 'b', type: [Type], offset: 8 },
|
|
33
|
+
// c: { name: 'c', type: [Type], offset: 16 }
|
|
34
|
+
// }
|
|
35
|
+
// }
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Koffi also exposes a few more utility functions to get a subset of this information:
|
|
39
|
+
|
|
40
|
+
- `koffi.sizeof(type)` to get the size of a type
|
|
41
|
+
- `koffi.alignof(type)` to get the alignment of a type
|
|
42
|
+
- `koffi.offsetof(type, member_name)` to get the offset of a record member
|
|
43
|
+
- `koffi.type(type)` to get the resolved type object from a type string
|
|
44
|
+
|
|
45
|
+
Just like before, you can refer to primitive types by their name or through `koffi.types`:
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
// These two lines do the same:
|
|
49
|
+
console.log(koffi.sizeof('long'));
|
|
50
|
+
console.log(koffi.sizeof(koffi.types.long));
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
# Aliases
|
|
54
|
+
|
|
55
|
+
You can alias a type with `koffi.alias(name, type)`. Aliased types are completely equivalent.
|
|
56
|
+
|
|
57
|
+
# Circular references
|
|
58
|
+
|
|
59
|
+
In some cases, composite types can point to each other and thus depend on each other. This can also happen when a function takes a pointer to a struct that also contains a function pointer.
|
|
60
|
+
|
|
61
|
+
To deal with this, you can create an opaque type and redefine it later to a concrete struct or union type, as shown below.
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
const Type1 = koffi.opaque('Type1');
|
|
65
|
+
|
|
66
|
+
const Type2 = koffi.struct('Type2', {
|
|
67
|
+
ptr: 'Type1 *',
|
|
68
|
+
i: 'int'
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Redefine Type1 to a concrete type
|
|
72
|
+
koffi.struct(Type1, {
|
|
73
|
+
ptr: 'Type2 *',
|
|
74
|
+
f: 'float'
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
> [!NOTE]
|
|
79
|
+
> You must use a proper type object when you redefine the type. If you only have the name, use `koffi.type()` to get a type object from a type string.
|
|
80
|
+
>
|
|
81
|
+
> ```js
|
|
82
|
+
> const MyType = koffi.opaque('MyType');
|
|
83
|
+
>
|
|
84
|
+
> // This does not work, you must use the MyType object and not a type string
|
|
85
|
+
> koffi.struct('MyType', {
|
|
86
|
+
> ptr: 'Type2 *',
|
|
87
|
+
> f: 'float'
|
|
88
|
+
> });
|
package/doc/unions.md
CHANGED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
# Union definition
|
|
2
|
-
|
|
3
|
-
*New in Koffi 2.5*
|
|
4
|
-
|
|
5
|
-
You can declare unions with a syntax similar to structs, but with the `koffi.union()` function. This function takes two arguments: the first one is the name of the type, and the second one is an object containing the union member names and types. You can omit the first argument to declare an anonymous union.
|
|
6
|
-
|
|
7
|
-
The following example illustrates how to declare the same union in C and in JS with Koffi:
|
|
8
|
-
|
|
9
|
-
```c
|
|
10
|
-
typedef union IntOrDouble {
|
|
11
|
-
int64_t i;
|
|
12
|
-
double d;
|
|
13
|
-
} IntOrDouble;
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
```js
|
|
17
|
-
const IntOrDouble = koffi.union('IntOrDouble', {
|
|
18
|
-
i: 'int64_t',
|
|
19
|
-
d: 'double'
|
|
20
|
-
});
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
# Input unions
|
|
24
|
-
|
|
25
|
-
## Passing union values to C
|
|
26
|
-
|
|
27
|
-
You can instantiate an union object with `koffi.Union(type)`. This will create a special object that contains at most one active member.
|
|
28
|
-
|
|
29
|
-
Once you have created an instance of your union, you can simply set the member with the dot operator as you would with a basic object. Then, simply pass your union value to the C function you wish.
|
|
30
|
-
|
|
31
|
-
```js
|
|
32
|
-
const U = koffi.union('U', { i: 'int', str: 'char *' });
|
|
33
|
-
|
|
34
|
-
const DoSomething = lib.func('void DoSomething(const char *type, U u)');
|
|
35
|
-
|
|
36
|
-
const u1 = new koffi.Union('U'); u1.i = 42;
|
|
37
|
-
const u2 = new koffi.Union('U'); u2.str = 'Hello!';
|
|
38
|
-
|
|
39
|
-
DoSomething('int', u1);
|
|
40
|
-
DoSomething('string', u2);
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
For simplicity, Koffi also accepts object literals with one property (no more, no less) setting the corresponding union member. The example belows uses this to simplify the code shown above:
|
|
44
|
-
|
|
45
|
-
```js
|
|
46
|
-
const U = koffi.union('U', { i: 'int', str: 'char *' });
|
|
47
|
-
|
|
48
|
-
const DoSomething = lib.func('void DoSomething(const char *type, U u)');
|
|
49
|
-
|
|
50
|
-
DoSomething('int', { i: 42 });
|
|
51
|
-
DoSomething('string', { str: 'Hello!' });
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Win32 example
|
|
55
|
-
|
|
56
|
-
The following example uses the [SendInput](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput) Win32 API to emit the Win+D shortcut and hide windows (show the desktop).
|
|
57
|
-
|
|
58
|
-
```js
|
|
59
|
-
import koffi from 'koffi';
|
|
60
|
-
// CJS: const koffi = require('koffi');
|
|
61
|
-
|
|
62
|
-
// Win32 type and functions
|
|
63
|
-
|
|
64
|
-
const user32 = koffi.load('user32.dll');
|
|
65
|
-
|
|
66
|
-
const INPUT_MOUSE = 0;
|
|
67
|
-
const INPUT_KEYBOARD = 1;
|
|
68
|
-
const INPUT_HARDWARE = 2;
|
|
69
|
-
|
|
70
|
-
const KEYEVENTF_KEYUP = 0x2;
|
|
71
|
-
const KEYEVENTF_SCANCODE = 0x8;
|
|
72
|
-
|
|
73
|
-
const VK_LWIN = 0x5B;
|
|
74
|
-
const VK_D = 0x44;
|
|
75
|
-
|
|
76
|
-
const MOUSEINPUT = koffi.struct('MOUSEINPUT', {
|
|
77
|
-
dx: 'long',
|
|
78
|
-
dy: 'long',
|
|
79
|
-
mouseData: 'uint32_t',
|
|
80
|
-
dwFlags: 'uint32_t',
|
|
81
|
-
time: 'uint32_t',
|
|
82
|
-
dwExtraInfo: 'uintptr_t'
|
|
83
|
-
});
|
|
84
|
-
const KEYBDINPUT = koffi.struct('KEYBDINPUT', {
|
|
85
|
-
wVk: 'uint16_t',
|
|
86
|
-
wScan: 'uint16_t',
|
|
87
|
-
dwFlags: 'uint32_t',
|
|
88
|
-
time: 'uint32_t',
|
|
89
|
-
dwExtraInfo: 'uintptr_t'
|
|
90
|
-
});
|
|
91
|
-
const HARDWAREINPUT = koffi.struct('HARDWAREINPUT', {
|
|
92
|
-
uMsg: 'uint32_t',
|
|
93
|
-
wParamL: 'uint16_t',
|
|
94
|
-
wParamH: 'uint16_t'
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
const INPUT = koffi.struct('INPUT', {
|
|
98
|
-
type: 'uint32_t',
|
|
99
|
-
u: koffi.union({
|
|
100
|
-
mi: MOUSEINPUT,
|
|
101
|
-
ki: KEYBDINPUT,
|
|
102
|
-
hi: HARDWAREINPUT
|
|
103
|
-
})
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
const SendInput = user32.func('unsigned int __stdcall SendInput(unsigned int cInputs, INPUT *pInputs, int cbSize)');
|
|
107
|
-
|
|
108
|
-
// Show/hide desktop with Win+D shortcut
|
|
109
|
-
|
|
110
|
-
let events = [
|
|
111
|
-
make_keyboard_event(VK_LWIN, true),
|
|
112
|
-
make_keyboard_event(VK_D, true),
|
|
113
|
-
make_keyboard_event(VK_D, false),
|
|
114
|
-
make_keyboard_event(VK_LWIN, false)
|
|
115
|
-
];
|
|
116
|
-
|
|
117
|
-
SendInput(events.length, events, koffi.sizeof(INPUT));
|
|
118
|
-
|
|
119
|
-
// Utility
|
|
120
|
-
|
|
121
|
-
function make_keyboard_event(vk, down) {
|
|
122
|
-
let event = {
|
|
123
|
-
type: INPUT_KEYBOARD,
|
|
124
|
-
u: {
|
|
125
|
-
ki: {
|
|
126
|
-
wVk: vk,
|
|
127
|
-
wScan: 0,
|
|
128
|
-
dwFlags: down ? 0 : KEYEVENTF_KEYUP,
|
|
129
|
-
time: 0,
|
|
130
|
-
dwExtraInfo: 0
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
return event;
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
# Output unions
|
|
140
|
-
|
|
141
|
-
Unlike structs, Koffi does not know which union member is valid, and it cannot decode it automatically. You can however use special `koffi.Union` objects for output parameters, and decode the memory after the call.
|
|
142
|
-
|
|
143
|
-
To decode an output union pointer parameter, create a placeholder object with `new koffi.Union(type)` and pass the resulting object to the function.
|
|
144
|
-
|
|
145
|
-
After the call, you can dereference the member value you want on this object and Koffi will decode it at this moment.
|
|
146
|
-
|
|
147
|
-
The following example illustrates the use of `koffi.Union()` to decode output unions after the call.
|
|
148
|
-
|
|
149
|
-
```c
|
|
150
|
-
#include <stdint.h>
|
|
151
|
-
|
|
152
|
-
typedef union IntOrDouble {
|
|
153
|
-
int64_t i;
|
|
154
|
-
double d;
|
|
155
|
-
} IntOrDouble;
|
|
156
|
-
|
|
157
|
-
void SetUnionInt(int64_t i, IntOrDouble *out)
|
|
158
|
-
{
|
|
159
|
-
out->i = i;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
void SetUnionDouble(double d, IntOrDouble *out)
|
|
163
|
-
{
|
|
164
|
-
out->d = d;
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
```js
|
|
169
|
-
const IntOrDouble = koffi.union('IntOrDouble', {
|
|
170
|
-
i: 'int64_t',
|
|
171
|
-
d: 'double',
|
|
172
|
-
raw: koffi.array('uint8_t', 8)
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
const SetUnionInt = lib.func('void SetUnionInt(int64_t i, _Out_ IntOrDouble *out)');
|
|
176
|
-
const SetUnionDouble = lib.func('void SetUnionDouble(double d, _Out_ IntOrDouble *out)');
|
|
177
|
-
|
|
178
|
-
let u1 = new koffi.Union('IntOrDouble');
|
|
179
|
-
let u2 = new koffi.Union('IntOrDouble');
|
|
180
|
-
|
|
181
|
-
SetUnionInt(123, u1);
|
|
182
|
-
SetUnionDouble(123, u2);
|
|
183
|
-
|
|
184
|
-
console.log(u1.i, '---', u1.raw); // Prints 123 --- Uint8Array(8) [123, 0, 0, 0, 0, 0, 0, 0]
|
|
185
|
-
console.log(u2.d, '---', u2.raw); // Prints 123 --- Uint8Array(8) [0, 0, 0, 0, 0, 0, 69, 64]
|
|
186
|
-
```
|
package/doc/values.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Decode to JS values
|
|
2
|
+
|
|
3
|
+
## Decode numbers
|
|
4
|
+
|
|
5
|
+
*New in Koffi 3.1.0*
|
|
6
|
+
|
|
7
|
+
Use these functions to decode numeric values from a pointer. These functions match the [number types](primitives#number-types) of Koffi:
|
|
8
|
+
|
|
9
|
+
Type | Function | Result
|
|
10
|
+
--------- | ---------------------------- | ------------------------------------
|
|
11
|
+
char | `koffi.decode.char(ptr)` | Number
|
|
12
|
+
uchar | `koffi.decode.uchar(ptr)` | Number
|
|
13
|
+
short | `koffi.decode.short(ptr)` | Number
|
|
14
|
+
ushort | `koffi.decode.ushort(ptr)` | Number
|
|
15
|
+
int | `koffi.decode.int(ptr)` | Number
|
|
16
|
+
uint | `koffi.decode.uint(ptr)` | Number
|
|
17
|
+
long | `koffi.decode.long(ptr)` | Number or BigInt (on LP64 platforms)
|
|
18
|
+
ulong | `koffi.decode.ulong(ptr)` | Number or BigInt (on LP64 platforms)
|
|
19
|
+
longlong | `koffi.decode.longlong(ptr)` | Number or BigInt
|
|
20
|
+
ulonglong | `koffi.decode.ulonglong(ptr)`| Number or BigInt
|
|
21
|
+
int8 | `koffi.decode.int8(ptr)` | Number
|
|
22
|
+
uint8 | `koffi.decode.uint8(ptr)` | Number
|
|
23
|
+
int16 | `koffi.decode.int16(ptr)` | Number
|
|
24
|
+
uint16 | `koffi.decode.uint16(ptr)` | Number
|
|
25
|
+
int32 | `koffi.decode.int32(ptr)` | Number
|
|
26
|
+
uint32 | `koffi.decode.uint32(ptr)` | Number
|
|
27
|
+
int64 | `koffi.decode.int64(ptr)` | Number or BigInt
|
|
28
|
+
uint64 | `koffi.decode.uint64(ptr)` | Number or BigInt
|
|
29
|
+
float | `koffi.decode.float(ptr)` | Number
|
|
30
|
+
double | `koffi.decode.double(ptr)` | Number
|
|
31
|
+
|
|
32
|
+
You can also decode [endian-sensitive integers](primitives#endian-sensitive-integers) with these functions:
|
|
33
|
+
|
|
34
|
+
Type | Function | Endianness | Result
|
|
35
|
+
--------- | ---------------------------- | ------------- | ----------------
|
|
36
|
+
int16_le | `koffi.decode.int16le(ptr)` | Little Endian | Number
|
|
37
|
+
uint16_le | `koffi.decode.uint16le(ptr)` | Little Endian | Number
|
|
38
|
+
int32_le | `koffi.decode.int32le(ptr)` | Little Endian | Number
|
|
39
|
+
uint32_le | `koffi.decode.uint32le(ptr)` | Little Endian | Number
|
|
40
|
+
int64_le | `koffi.decode.int64le(ptr)` | Little Endian | Number or BigInt
|
|
41
|
+
uint64_le | `koffi.decode.uint64le(ptr)` | Little Endian | Number or BigInt
|
|
42
|
+
int16_be | `koffi.decode.int16be(ptr)` | Big Endian | Number
|
|
43
|
+
uint16_be | `koffi.decode.uint16be(ptr)` | Big Endian | Number
|
|
44
|
+
int32_be | `koffi.decode.int32be(ptr)` | Big Endian | Number
|
|
45
|
+
uint32_be | `koffi.decode.uint32be(ptr)` | Big Endian | Number
|
|
46
|
+
int64_be | `koffi.decode.int64be(ptr)` | Big Endian | Number or BigInt
|
|
47
|
+
uint64_be | `koffi.decode.uint64be(ptr)` | Big Endian | Number or BigInt
|
|
48
|
+
|
|
49
|
+
## Decode strings
|
|
50
|
+
|
|
51
|
+
You can also decode strings, NUL-terminated or with an explicit length:
|
|
52
|
+
|
|
53
|
+
Type | Function | Conversion/encoding
|
|
54
|
+
-------- | ------------------------------------ | -------------------------------------
|
|
55
|
+
string | `koffi.decode.string(ptr)` | UTF-8 (NUL-terminated)
|
|
56
|
+
string | `koffi.decode.string(ptr, length)` | UTF-8 (explicit length)
|
|
57
|
+
string16 | `koffi.decode.string16(ptr)` | UTF-16 (NUL-terminated)
|
|
58
|
+
string16 | `koffi.decode.string16(ptr, length)` | UTF-16 (explicit length)
|
|
59
|
+
string32 | `koffi.decode.string32(ptr)` | UTF-32 (NUL-terminated)
|
|
60
|
+
string32 | `koffi.decode.string32(ptr, length)` | UTF-32 (explicit length)
|
|
61
|
+
wstring | `koffi.decode.wstring(ptr)` | UTF-16 or UTF-32 (platform-dependent)
|
|
62
|
+
wstring | `koffi.decode.wstring(ptr, length)` | UTF-16 or UTF-32 (platform-dependent)
|
|
63
|
+
|
|
64
|
+
## Generic decode function
|
|
65
|
+
|
|
66
|
+
Use `koffi.decode()` to decode the data pointed to by a pointer (represented by a BigInt value).
|
|
67
|
+
|
|
68
|
+
Some arguments are optional and this function can be called in several ways:
|
|
69
|
+
|
|
70
|
+
- `koffi.decode(ptr, type)`: no offset
|
|
71
|
+
- `koffi.decode(ptr, offset, type)`: explicit offset to add to the pointer before decoding
|
|
72
|
+
|
|
73
|
+
By default, Koffi expects NUL terminated strings when decoding them. See below if you need to specify the string length.
|
|
74
|
+
|
|
75
|
+
The following example illustrates how to decode an integer and a C string variable.
|
|
76
|
+
|
|
77
|
+
```c
|
|
78
|
+
int my_int = 42;
|
|
79
|
+
const char *my_string = "foobar";
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```js
|
|
83
|
+
const my_int = lib.symbol('my_int');
|
|
84
|
+
const my_string = lib.symbol('my_string');
|
|
85
|
+
|
|
86
|
+
console.log(koffi.decode(my_int, 'int')) // Prints 42
|
|
87
|
+
console.log(koffi.decode(my_string, 'const char *')) // Prints "foobar"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
There is also an optional ending `length` argument that you can use in two cases:
|
|
91
|
+
|
|
92
|
+
- Use it to give the number of bytes to decode in non-NUL terminated strings: `koffi.decode(ptr, 'char *', 5)`
|
|
93
|
+
- Decode consecutive values into an array. For example, here is how you can decode an array with 3 float values: `koffi.decode(ptr, 'float', 3)`. This is equivalent to `koffi.decode(ptr, koffi.array('float', 3))`.
|
|
94
|
+
|
|
95
|
+
The example below will decode the symbol `my_string` defined above but only the first three bytes.
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
// Only decode 3 bytes from the C string my_string
|
|
99
|
+
console.log(koffi.decode(my_string, 'const char *', 3)) // Prints "foo"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
# Encode to C memory
|
|
103
|
+
|
|
104
|
+
Use `koffi.encode()` to encode JS values into C symbols or pointers, which are represented by BigInt numbers.
|
|
105
|
+
|
|
106
|
+
Some arguments are optional and this function can be called in several ways:
|
|
107
|
+
|
|
108
|
+
- `koffi.encode(ptr, type, value)`: no offset
|
|
109
|
+
- `koffi.encode(ptr, offset, type, value)`: explicit offset to add to the pointer before encoding
|
|
110
|
+
|
|
111
|
+
We'll reuse the examples shown above and change the variable values with `koffi.encode()`.
|
|
112
|
+
|
|
113
|
+
```c
|
|
114
|
+
int my_int = 42;
|
|
115
|
+
const char *my_string = NULL;
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
const my_int = lib.symbol('my_int');
|
|
120
|
+
const my_string = lib.symbol('my_string');
|
|
121
|
+
|
|
122
|
+
console.log(koffi.decode(my_int, 'int')) // Prints 42
|
|
123
|
+
console.log(koffi.decode(my_string, 'const char *')) // Prints null
|
|
124
|
+
|
|
125
|
+
koffi.encode(my_int, 'int', -1);
|
|
126
|
+
koffi.encode(my_string, 'const char *', 'Hello World!');
|
|
127
|
+
|
|
128
|
+
console.log(koffi.decode(my_int, 'int')) // Prints -1
|
|
129
|
+
console.log(koffi.decode(my_string, 'const char *')) // Prints "Hello World!"
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
When encoding strings (either directly or embedded in arrays or structs), the memory will be bound to the raw pointer value and managed by Koffi. You can assign to the same string again and again without any leak or risk of use-after-free.
|
|
133
|
+
|
|
134
|
+
There is also an optional ending `length` argument that you can use to encode an array. For example, here is how you can encode an array with 3 float values: `koffi.encode(symbol, 'float', [1, 2, 3], 3)`. This is equivalent to `koffi.encode(symbol, koffi.array('float', 3), [1, 2, 3])`.
|