Union数据

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]

Last updated