1 javascript c winapi node.js node-ffi
我想在 nodejs 中使用 Windows Api 中的 SendInput 函数,使用 FFI 包。
我对 C 的了解有限,所以我无法真正弄清楚我有什么问题,我基本上是在尝试虚拟按下键盘上的一个键。
这是我的代码:
var ffi = require('ffi');
var ref = require ('ref');
var struct = require ('ref-struct');
var keyboardInput = struct({
'type': 'int',
'wVK': 'int',
'wScan': 'int',
'dwFlags': 'int',
'time': 'int',
'dwExtraInfo': 'int64'
});
var keyboardInputPtr = ref.refType(keyboardInput);
var keyboard = new keyboardInput();
keyboard.type = 1;
keyboard.wVK = 0x41;
keyboard.wScan = 0;
keyboard.dwFlags = 2;
keyboard.time = 0;
keyboard.dwExtraInfo = 0;
var user32 = ffi.Library('user32', {
'SendInput': [ 'int', [ 'uint', keyboardInputPtr, 'int' ] ]
});
setInterval(function(){
var r = user32.SendInput(1, keyboard.ref(), 40);
console.log(r);
}, 500);
Run Code Online (Sandbox Code Playgroud)
它在控制台中给我记录了一个“1”,这不应该意味着它可以工作吗?因为当我打开记事本时没有按下任何键。
我终于找到了一种使用node-ffi/node-ffi-napi使用该SendInput功能输入按键的方法!(下面的当前代码使用node-ffi-napi,因为node-ffi已无人维护/损坏;请参阅node-ffi版本的编辑历史记录,api 几乎完全相同)
但是,请注意,您可以通过两种方式调用 SendInput 函数,如下所示:https ://autohotkey.com/boards/viewtopic.php?p=213617#p213617
就我而言,我不得不使用第二种(扫描码)方法,因为第一种(虚拟密钥)方法在我需要密钥模拟的程序中不起作用。
事不宜迟,这里是完整的解决方案:
import keycode from "keycode";
import ffi from "ffi-napi";
import ref from "ref-napi";
import os from "os";
import import_Struct from "ref-struct-di";
var arch = os.arch();
const Struct = import_Struct(ref);
var Input = Struct({
"type": "int",
// For some reason, the wScan value is only recognized as the wScan value when we add this filler slot.
// It might be because it's expecting the values after this to be inside a "wrapper" substructure, as seen here:
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms646270(v=vs.85).aspx
"???": "int",
"wVK": "short",
"wScan": "short",
"dwFlags": "int",
"time": "int",
"dwExtraInfo": "int64"
});
var user32 = ffi.Library("user32", {
SendInput: ["int", ["int", Input, "int"]],
//MapVirtualKeyEx: ["uint", ["uint", "uint", intPtr]],
});
const extendedKeyPrefix = 0xe000;
const INPUT_KEYBOARD = 1;
const KEYEVENTF_EXTENDEDKEY = 0x0001;
const KEYEVENTF_KEYUP = 0x0002;
const KEYEVENTF_UNICODE = 0x0004;
const KEYEVENTF_SCANCODE = 0x0008;
//const MAPVK_VK_TO_VSC = 0;
export class KeyToggle_Options {
asScanCode = true;
keyCodeIsScanCode = false;
flags?: number;
async = false; // async can reduce stutter in your app, if frequently sending key-events
}
let entry = new Input(); // having one persistent native object, and just changing its fields, is apparently faster (from testing)
entry.type = INPUT_KEYBOARD;
entry.time = 0;
entry.dwExtraInfo = 0;
export function KeyToggle(keyCode: number, type = "down" as "down" | "up", options?: Partial<KeyToggle_Options>) {
const opt = Object.assign({}, new KeyToggle_Options(), options);
// scan-code approach (default)
if (opt.asScanCode) {
//let scanCode = user32.MapVirtualKeyEx(keyCode, MAPVK_VK_TO_VSC); // this should work, but it had a Win32 error (code 127) for me
let scanCode = opt.keyCodeIsScanCode ? keyCode : ConvertKeyCodeToScanCode(keyCode);
let isExtendedKey = (scanCode & extendedKeyPrefix) == extendedKeyPrefix;
entry.dwFlags = KEYEVENTF_SCANCODE;
if (isExtendedKey) {
entry.dwFlags |= KEYEVENTF_EXTENDEDKEY;
}
entry.wVK = 0;
entry.wScan = isExtendedKey ? scanCode - extendedKeyPrefix : scanCode;
}
// (virtual) key-code approach
else {
entry.dwFlags = 0;
entry.wVK = keyCode;
//info.wScan = 0x0200;
entry.wScan = 0;
}
if (opt.flags != null) {
entry.dwFlags = opt.flags;
}
if (type == "up") {
entry.dwFlags |= KEYEVENTF_KEYUP;
}
if (opt.async) {
return new Promise((resolve, reject)=> {
user32.SendInput.async(1, entry, arch === "x64" ? 40 : 28, (error, result)=> {
if (error) reject(error);
resolve(result);
});
});
}
return user32.SendInput(1, entry, arch === "x64" ? 40 : 28);
}
export function KeyTap(keyCode: number, opt?: Partial<KeyToggle_Options>) {
KeyToggle(keyCode, "down", opt);
KeyToggle(keyCode, "up", opt);
}
// Scan-code for a char equals its index in this list. List based on: https://qb64.org/wiki/Scancodes, https://www.qbasic.net/en/reference/general/scan-codes.htm
// Not all keys are in this list, of course. You can add a custom mapping for other keys to the function below it, as needed.
let keys = "**1234567890-=**qwertyuiop[]**asdfghjkl;'`*\\zxcvbnm,./".split("");
export function ConvertKeyCodeToScanCode(keyCode: number) {
let keyChar = String.fromCharCode(keyCode).toLowerCase();
let result = keys.indexOf(keyChar);
console.assert(result != -1, `Could not find scan-code for key ${keyCode} (${keycode.names[keyCode]}).`)
return result;
}
Run Code Online (Sandbox Code Playgroud)
要使用它,请调用:
KeyTap(65); // press the A key
Run Code Online (Sandbox Code Playgroud)
或者,如果您使用的是keycode npm 包:
import keycode from "keycode";
KeyTap(keycode.codes.a);
Run Code Online (Sandbox Code Playgroud)
这是一个按下a键的工作示例。它采用ref-struct-napi并ref-union-napi准确地表示INPUT结构。
const FFI = require('ffi-napi')
const StructType = require('ref-struct-napi')
const UnionType = require('ref-union-napi')
const ref = require('ref-napi')
const user32 = new FFI.Library('user32.dll', {
// UINT SendInput(
// _In_ UINT cInputs, // number of input in the array
// _In_reads_(cInputs) LPINPUT pInputs, // array of inputs
// _In_ int cbSize); // sizeof(INPUT)
'SendInput': ['uint32', ['int32', 'pointer', 'int32']],
})
// typedef struct tagMOUSEINPUT {
// LONG dx;
// LONG dy;
// DWORD mouseData;
// DWORD dwFlags;
// DWORD time;
// ULONG_PTR dwExtraInfo;
// } MOUSEINPUT;
const MOUSEINPUT = StructType({
dx: 'int32',
dy: 'int32',
mouseData: 'uint32',
flags: 'uint32',
time: 'uint32',
extraInfo: 'pointer',
})
// typedef struct tagKEYBDINPUT {
// WORD wVk;
// WORD wScan;
// DWORD dwFlags;
// DWORD time;
// ULONG_PTR dwExtraInfo;
// } KEYBDINPUT;
const KEYBDINPUT = StructType({
vk: 'uint16',
scan: 'uint16',
flags: 'uint32',
time: 'uint32',
extraInfo: 'pointer',
})
// typedef struct tagHARDWAREINPUT {
// DWORD uMsg;
// WORD wParamL;
// WORD wParamH;
// } HARDWAREINPUT;
const HARDWAREINPUT = StructType({
msg: 'uint32',
paramL: 'uint16',
paramH: 'uint16',
})
// typedef struct tagINPUT {
// DWORD type;
// union
// {
// MOUSEINPUT mi;
// KEYBDINPUT ki;
// HARDWAREINPUT hi;
// } DUMMYUNIONNAME;
// } INPUT;
const INPUT_UNION = UnionType({
mi: MOUSEINPUT,
ki: KEYBDINPUT,
hi: HARDWAREINPUT,
})
const INPUT = StructType({
type: 'uint32',
union: INPUT_UNION,
})
const pressKey = (scanCode) => {
const keyDownKeyboardInput = KEYBDINPUT({vk: 0, extraInfo: ref.NULL_POINTER, time: 0, scan: scanCode, flags: 0x0008})
const keyDownInput = INPUT({type: 1, union: INPUT_UNION({ki: keyDownKeyboardInput})})
user32.SendInput(1, keyDownInput.ref(), INPUT.size)
const keyUpKeyboardInput = KEYBDINPUT({vk: 0, extraInfo: ref.NULL_POINTER, time: 0, scan: scanCode, flags: 0x0008 | 0x0002})
const keyUpInput = INPUT({type: 1, union: INPUT_UNION({ki: keyUpKeyboardInput})})
user32.SendInput(1, keyUpInput.ref(), INPUT.size)
}
pressKey(0x1E)
Run Code Online (Sandbox Code Playgroud)
如果要执行SendInput包含多次按键的单个调用,请构造一个INPUT结构数组:
const pressKey = (scanCode) => {
const inputCount = 2
const inputArray = Buffer.alloc(INPUT.size * inputCount)
const keyDownKeyboardInput = KEYBDINPUT({vk: 0, extraInfo: ref.NULL_POINTER, time: 0, scan: scanCode, flags: 0x0008})
const keyDownInput = INPUT({type: 1, union: INPUT_UNION({ki: keyDownKeyboardInput})})
keyDownInput.ref().copy(inputArray, 0)
const keyUpKeyboardInput = KEYBDINPUT({vk: 0, extraInfo: ref.NULL_POINTER, time: 0, scan: scanCode, flags: 0x0008 | 0x0002})
const keyUpInput = INPUT({type: 1, union: INPUT_UNION({ki: keyUpKeyboardInput})})
keyUpInput.ref().copy(inputArray, INPUT.size)
user32.SendInput(inputCount, inputArray, INPUT.size)
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2055 次 |
| 最近记录: |