Windows
Node.js
ffi

Windows上のNode.jsでffiを使ってキーボード入力をする

More than 1 year has passed since last update.

いわゆるキーボード入力をエミュレートするというものです。

準備

node-gypが動作する環境を整えておいてください。コンパイル環境は管理者権限のコマンドプロンプトで

npm install --global --production windows-build-tools

と実行すればできるようになります。1

必要なnpmパッケージは次の五つです。それぞれ入れてください。

  • ffi
  • ref
  • ref-struct
  • ref-union
  • ref-array

ffiがDLLを直接読み込んで実行します。refおよびref-*は構造体、共用体、配列などを行うのに必要になります。

コード

面倒だったのでCoffeeScriptで書いています。CoffeeScriptを知らないって人はTry CoffeeScriptで変換されたコードを見てください。2

winkey_send.coffee
import ref from 'ref'
import ffi from 'ffi'
import StructType from 'ref-struct'
import UnionType from 'ref-union'
import ArrayType from 'ref-array'

###
SendInput function
  https://msdn.microsoft.com/library/windows/desktop/ms646310.aspx
###

# check pointer size for defined ULONG_PTR
ptrSize = ref.NULL_POINTER.length
ulongPtr = switch ptrSize
  when 4
    ref.types.uint32
  when 8
    ref.types.uint64
  else
    throw new Error("Supported pointer size is 4 or 8, but is #{ptrSize}")

MouseInputType = StructType
  dx: ref.types.long
  dy: ref.types.long
  mouseData: ref.types.uint32
  dwFlags: ref.types.uint32
  time: ref.types.uint32
  dwExtraInfo: ulongPtr

KeybdInputType = StructType
  wVk: ref.types.uint16
  wScan: ref.types.uint16
  dwFlags: ref.types.uint32
  time: ref.types.uint32
  dwExtraInfo: ulongPtr

HardwareInputType = StructType
  uMsg: ref.types.uint32
  wParamL: ref.types.uint16
  wParamH: ref.types.uint16

UnionInputType = UnionType
  mi: MouseInputType
  ki: KeybdInputType
  hi: HardwareInputType

InputType = StructType
  type: ref.types.uint32
  u: UnionInputType

InputArrayType = ArrayType(InputType)

user32 = ffi.Library 'user32',
  SendInput: [ref.types.uint, [ref.types.uint, InputArrayType, ref.types.int]]

# Virtual-Key Codes
#   https://msdn.microsoft.com/library/windows/desktop/dd375731.aspx
keys = [0x48, 0x4F, 0x47, 0x45] # hoge
inputs = new InputArrayType(keys.length)

for vk, i in keys
  inputs[i].type = 1 # INPUT_KEYBOARD
  inputs[i].u.ki.wVk = vk
  inputs[i].u.ki.wScan = 0
  inputs[i].u.ki.dwFlags = 0
  inputs[i].u.ki.time = 0
  inputs[i].u.ki.dw = 0

setInterval ->
  result = user32.SendInput(inputs.length, inputs, InputType.size)
  console.log("send: #{result}")
, 1000

解説

SendInput関数を使用しています。Cのコードをffiを用いて実現しているだけに過ぎません。

マウスもできると思いますが、試していません。他にもあまりテストしていませんので、うまくいかなかったら、コメント等残してくれると助かります。特に、32bit環境は試す環境が無いので、うまくいくかはかなり不安です。

終了はCtrl+Cなどで強制終了してください。

構造体、共用体、配列が組み合わさった場合にffiにどう読み込ませるかのサンプルが全然見当たらなかったので、本当にこれでいいのかはちょっと不安です。一応、上のコードは手元では動いてはいるのですが。

参考

Node.jsでWindowsのキー状態を取得する
目的は逆ですが、ffiの使い方の参考にさせていただきました。


  1. Python 2.7のインストール先は実行ユーザーのプロファイルになります。インストール時の管理者と実際使うユーザーが異なる場合は手動で準備した方が無難でしょう。 

  2. だれか、ES2015+で整形して書き直したのに置き換えてくれないかな。