Koffi 2.5 中的新功能
Union定义
您可以使用与结构类似的语法来声明Union,但使用函数koffi.union()
。该函数有两个参数:第一个是类型的名称,第二个是包含Union成员名称和类型的对象。您可以省略第一个参数来声明匿名Union。
以下示例说明了如何使用 Koffi 在 C 和 JS 中声明相同的Union:
typedef union IntOrDouble {
int64_t i;
double d;
} IntOrDouble;
const IntOrDouble = koffi.union('IntOrDouble', {
i: 'int64_t',
d: 'double'
});
输入Union
将联合值传递给 C
您可以使用 实例化一个Union对象koffi.Union(type)
。这将创建一个最多包含一个活动成员的特殊对象。
创建Union实例后,您可以简单地使用点运算符设置成员,就像使用基本对象一样。然后,只需将您的Union值传递给您想要的 C 函数即可。
const U = koffi.union('U', { i: 'int', str: 'char *' });
const DoSomething = lib.func('void DoSomething(const char *type, U u)');
const u1 = new koffi.Union('U'); u1.i = 42;
const u2 = new koffi.Union('U'); u2.str = 'Hello!';
DoSomething('int', u1);
DoSomething('string', u2);
为简单起见,Koffi 还接受带有一个属性(不多不少)设置相应Union成员的对象字面量。下面的示例使用它来简化上面显示的代码。
const U = koffi.union('U', { i: 'int', str: 'char *' });
const DoSomething = lib.func('void DoSomething(const char *type, U u)');
DoSomething('int', { .i = 42 });
DoSomething('string', { .str = 'Hello!' });
Win32 示例
以下示例使用SendInput Win32 API 发出 Win+D 快捷方式并隐藏窗口(显示桌面)。
// ES6 syntax: import koffi from 'koffi';
const koffi = require('koffi');
// Win32 type and functions
const user32 = koffi.load('user32.dll');
const INPUT_MOUSE = 0;
const INPUT_KEYBOARD = 1;
const INPUT_HARDWARE = 2;
const KEYEVENTF_KEYUP = 0x2;
const KEYEVENTF_SCANCODE = 0x8;
const VK_LWIN = 0x5B;
const VK_D = 0x44;
const MOUSEINPUT = koffi.struct('MOUSEINPUT', {
dx: 'long',
dy: 'long',
mouseData: 'uint32_t',
dwFlags: 'uint32_t',
time: 'uint32_t',
dwExtraInfo: 'uintptr_t'
});
const KEYBDINPUT = koffi.struct('KEYBDINPUT', {
wVk: 'uint16_t',
wScan: 'uint16_t',
dwFlags: 'uint32_t',
time: 'uint32_t',
dwExtraInfo: 'uintptr_t'
});
const HARDWAREINPUT = koffi.struct('HARDWAREINPUT', {
uMsg: 'uint32_t',
wParamL: 'uint16_t',
wParamH: 'uint16_t'
});
const INPUT = koffi.struct('INPUT', {
type: 'uint32_t',
u: koffi.union({
mi: MOUSEINPUT,
ki: KEYBDINPUT,
hi: HARDWAREINPUT
})
});
const SendInput = user32.func('unsigned int __stdcall SendInput(unsigned int cInputs, INPUT *pInputs, int cbSize)');
// Show/hide desktop with Win+D shortcut
let events = [
make_keyboard_event(VK_LWIN, true),
make_keyboard_event(VK_D, true),
make_keyboard_event(VK_D, false),
make_keyboard_event(VK_LWIN, false)
];
SendInput(events.length, events, koffi.sizeof(INPUT));
// Utility
function make_keyboard_event(vk, down) {
let event = {
type: INPUT_KEYBOARD,
u: {
ki: {
wVk: vk,
wScan: 0,
dwFlags: down ? 0 : KEYEVENTF_KEYUP,
time: 0,
dwExtraInfo: 0
}
}
};
return event;
}
输出Union
与Struct不同,Koffi 不知道哪个Union成员是有效的,并且无法自动对其进行解码。但是,您可以使用特殊koffi.Union
对象作为输出参数,并在调用后对内存进行解码。
要解码输出Union指针参数,请创建一个占位符对象new koffi.Union(type)
并将结果对象传递给函数。
调用之后,您可以在该对象上取消引用您想要的成员值,Koffi 此时将对其进行解码。
以下示例说明了如何koffi.Union()
在调用后使用来解码输出Union。
#include <stdint.h>
typedef union IntOrDouble {
int64_t i;
double d;
} IntOrDouble;
void SetUnionInt(int64_t i, IntOrDouble *out)
{
out->i = i;
}
void SetUnionDouble(double d, IntOrDouble *out)
{
out->d = d;
}
const IntOrDouble = koffi.union('IntOrDouble', {
i: 'int64_t',
d: 'double',
raw: koffi.array('uint8_t', 8)
});
const SetUnionInt = lib.func('void SetUnionInt(int64_t i, _Out_ IntOrDouble *out)');
const SetUnionDouble = lib.func('void SetUnionDouble(double d, _Out_ IntOrDouble *out)');
let u1 = new koffi.Union('IntOrDouble');
let u2 = new koffi.Union('IntOrDouble');
SetUnionInt(123, u1);
SetUnionDouble(123, u2);
console.log(u1.i, '---', u1.raw); // Prints 123 --- Uint8Array(8) [123, 0, 0, 0, 0, 0, 0, 0]
console.log(u2.d, '---', u2.raw); // Prints 123 --- Uint8Array(8) [0, 0, 0, 0, 0, 0, 69, 64]