首页 > 解决方案 > 节点 ffi SetWindowsHookExW KeyboardProc 回调不适用于 32 位

问题描述

此代码在 x64 上运行良好(它是键盘输入事件的单独线程中的 windows 全局挂钩。在电子程序中使用它来实现条形码扫描功能),即 KBDLLHOOKSTRUCT 结构中的 keyboardPoc 接收器正确值:vkCode、scanCode 等。但是在 x86 上不是 - 任何按键总是有相同的值。我认为问题出在结构数据类型中并以不同的方式更改它们,但没有任何帮助。我一遍又一遍地测试了结果,所以我需要社区的帮助。

const os = require("os");
const ffi = require("ffi-napi");
const ref = require("ref-napi");
const Struct = require("ref-struct-di")(ref);


const is64bit = os.arch() === "x64";

// Types
const LONG = is64bit ? ref.types.long : ref.types.int32;
const ULONG = is64bit ? ref.types.ulong : ref.types.uint32;
const INT = ref.types.int;
const UINT = ref.types.uint;
const DWORD = ref.types.uint32; // DWORD always is unsigned 32-bit
const BOOL = ref.types.bool;

const HANDLE = is64bit ? ref.types.uint64 : ref.types.uint32;
const HHOOK = HANDLE;
const HWND = HANDLE;
const HINSTANCE = HANDLE;

const WPARAM = is64bit ? ref.types.uint64 : ref.types.uint32; // typedef UINT_PTR, uint32(x86) or uint64(64)
const LPARAM = is64bit ? ref.types.int64 : ref.types.int32; // typedef LONG_PTR, int32(x86) or int64(64)
const LRESULT = is64bit ? ref.types.int64 : ref.types.int32; // typedef LONG_PTR

const HOOKPROC = "pointer";

// Structures
const POINT = Struct({
  x: LONG,
  y: LONG,
});

const MSG = Struct({
  hwnd: HWND,
  message: UINT,
  wParam: WPARAM,
  lParam: LPARAM,
  time: DWORD,
  pt: POINT,
  lPrivate: DWORD,
});

const KBDLLHOOKSTRUCT = Struct({
  vkCode: DWORD,
  scanCode: DWORD,
  flags: DWORD,
  time: DWORD,
  dwExtraInfo: ULONG,
});

// User32 bindings
const user32 = ffi.Library("user32", {
  SetWindowsHookExW: [HHOOK, [INT, HOOKPROC, HINSTANCE, DWORD]],
  UnhookWindowsHookEx: [BOOL, [HHOOK]],
  CallNextHookEx: [LRESULT, [HHOOK, INT, WPARAM, LPARAM]],
  GetMessageW: [BOOL, [MSG, HWND, UINT, UINT]],
  TranslateMessage: [BOOL, [MSG]],
  DispatchMessageW: [LRESULT, [MSG]],
});

// Constants
const WH_KEYBOARD_LL = 13;
const WM_KEYDOWN = 0x0100; // 256
// const WM_KEYUP = 0x0101; // 257
// const WM_SYSKEYDOWN = 0x0104; // 260
// const WM_SYSKEYUP = 0x0105; // 261

let hHook = 0;

// Callback for LL(low-level) hook (doesn`t require dll injection)

// LRESULT CALLBACK LowLevelKeyboardProc(
//     _In_ int    nCode,
//     _In_ WPARAM wParam,
//     _In_ LPARAM lParam
//   );

const keyboardProc = ffi.Callback(
  LRESULT,
  [INT, WPARAM, KBDLLHOOKSTRUCT],
  (nCode, wParam, lParam) => {
    console.log(
      `input event callback: ${JSON.stringify(nCode)} ${JSON.stringify(
        wParam
      )} ${JSON.stringify(lParam)}`
    );
    if (wParam === WM_KEYDOWN) {
      const message = {
        event: "key-down",
        data: {
          vkCode: lParam.vkCode,
          scanCode: lParam.scanCode,
        },
      };

    }
    return user32.CallNextHookEx(
      hHook,
      nCode,
      wParam,
      ref.address(lParam.ref())
    );
  }
);

/* Set the hook */
hHook = user32.SetWindowsHookExW(WH_KEYBOARD_LL, keyboardProc, 0, 0);

/* Message loop */
const msg = new MSG();
const res = user32.GetMessageW(msg.ref(), 0, 0, 0);
while (res) {
  user32.TranslateMessage(msg.ref());
  user32.DispatchMessageW(msg.ref());
}

标签: javascriptnode.jsstructffi

解决方案


正确的类型keyboardProc必须是[INT, WPARAM, ref.refType(KBDLLHOOKSTRUCT)]。然后lParam.deref()显示正确的价值。感谢https://stackoverflow.com/a/69688272/11492379的低端,这对我有很大帮助。


推荐阅读