在Node-FFI中使用SendInput [英] using SendInput in Node-FFI

查看:54
本文介绍了在Node-FFI中使用SendInput的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用FFI包从nodejs的Windows Api中使用SendInput函数.

我对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); 

它在控制台中记录了一个"1",这不意味着它可以工作吗?因为打开记事本时没有按下任何键.

我终于找到了一种使用 node-ffi / node-ffi-napi 输入密钥的方法使用 SendInput 功能-按下!(下面的当前代码使用 node-ffi-napi ,因为 node-ffi 未被维护/损坏;请参见 node-ffi 版本的编辑历史记录,其api几乎完全相同)

但是,请注意,有两种方法可以调用SendInput函数,如下所示:

要使用它,请致电:

  KeyTap(65);//按下A键 

或者,如果您使用的是关键字代码npm包:

 从"keycode"导入密钥代码;KeyTap(keycode.codes.a); 

I wanted to use the SendInput function from the windows Api in nodejs, using the FFI package.

My knowledge of C is limited so I can't really figure out what problem I have, I'm basically trying to Virtually press a key on the keyboard.

That's the code I have:

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);

It logs me a "1" in the console, shouldn't that mean it works? Because I don't get a key pressed when I open notepad.

解决方案

I finally found a way to use node-ffi/node-ffi-napi to input key-presses using the SendInput function! (current code below uses node-ffi-napi, since node-ffi has been unmaintained/broken; see edit history for node-ffi version, the api is almost exactly the same)

However, note that there are two ways you can call the SendInput function, as seen here: https://autohotkey.com/boards/viewtopic.php?p=213617#p213617

In my case, I had to use the second (scan code) approach, because the first (virtual key) approach didn't work in the programs I needed the key simulation for.

Without further ado, here is the complete solution:

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;
}

To use it, call:

KeyTap(65); // press the A key

Or, if you're using the keycode npm package:

import keycode from "keycode";
KeyTap(keycode.codes.a);

这篇关于在Node-FFI中使用SendInput的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆