c++node.jswinapi

How to decode KBDLLHOOKSTRUCT from lParam using koffi in Node.js?


I'm trying to convert a C++ keyboard hook program to Node.js using the koffi library. In C++, I would typically cast the lParam parameter to a KBDLLHOOKSTRUCT pointer like this:

KBDLLHOOKSTRUCT *pKeyBoard = (KBDLLHOOKSTRUCT *)lParam;

Here’s the full c++

#include <windows.h>
#include <iostream>

// Global variable to store the hook handle
HHOOK hKeyboardHook;

// Callback function for handling keyboard events
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    std::cout << nCode << std::endl;
    std::cout << wParam << std::endl;
    std::cout << lParam << std::endl;
    if (nCode >= 0)
    { // The keyboard message is being processed
        if (wParam == WM_KEYDOWN)
        { // A key was pressed down
            KBDLLHOOKSTRUCT *pKeyBoard = (KBDLLHOOKSTRUCT *)lParam;
            DWORD vkCode = pKeyBoard->vkCode;
            std::cout << "Key pressed: " << vkCode << std::endl;

            // If the ESC key is pressed, exit the application
            if (vkCode == VK_ESCAPE)
            {
                PostQuitMessage(0);
            }
        }
    }
    return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}

int main()
{
    // Set a global keyboard hook
    hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, NULL, 0);

    if (hKeyboardHook == NULL)
    {
        std::cerr << "Failed to install hook!" << std::endl;
        return 1;
    }

    std::cout << "Hook installed successfully!" << std::endl;

    // Message loop
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    // Unhook when no longer needed
    UnhookWindowsHookEx(hKeyboardHook);

    return 0;
}

This allows me to access the virtual key code (and other data) from the KBDLLHOOKSTRUCT structure.

How can I achieve the same in Node.js using koffi? Below is my current code:

import koffi from "koffi"
import * as D from "win32-def/def"

const HHOOK = koffi.pointer("HHOOK", koffi.opaque())
const HINSTANCE = koffi.pointer("HINSTANCE", D.INT)

const LRESULT = koffi.alias("LRESULT", D.LONG_PTR)
const WPARAM = koffi.alias("WPARAM", D.ULONG_PTR)
const LPARAM = koffi.alias("LPARAM", D.LONG_PTR)

const KBDLLHOOKSTRUCT = koffi.struct("KBDLLHOOKSTRUCT", {
  vkCode: "DWORD",
  scanCode: "DWORD",
  flags: "DWORD",
  time: "DWORD",
  dwExtraInfo: "ULONG_PTR",
})
export const HOOKPROC = koffi.proto("HOOKPROC", "LRESULT", ["int", "WPARAM", "LPARAM"])

export const SetWindowsHookExA = user32.func("SetWindowsHookExA", "HHOOK", [D.INT, koffi.pointer(HOOKPROC), HINSTANCE, "DWORD"])

const WH_KEYBOARD_LL = 13

export const installMouseHook = () => {
  if (!hHook) {
    const hInstance = GetModuleHandle(null)
    hHook = SetWindowsHookExA(WH_KEYBOARD_LL, onHookProc, null, 0)
    if (!hHook) {
      throw new Error("Failed to install keyboard hook")
    }
    console.log("Keyboard hook installed.")
  }
}

// Hook callback function
const onHookProc = koffi.register((nCode, wParam, lParam) => {
  console.log(nCode, wParam, lParam)

  let kb = koffi.decode(lParam, "KBDLLHOOKSTRUCT") // <====not work
  let kb1= koffi.as(lParam, "KBDLLHOOKSTRUCT") //<====not work

  return CallNextHookEx(hHook, nCode, wParam, lParam)
}, koffi.pointer(HOOKPROC))

I tried using koffi.decode and koffi.as, but I wasn't able to correctly convert the lParam to access the KBDLLHOOKSTRUCT structure in Node.js.

Any guidance would be appreciated!


Solution

  • After numerous attempts, I finally found the solution.

    const LPARAM = koffi.pointer("LPARAM", D.LPARAM)
    
    let info = koffi.decode(lParam, "KBDLLHOOKSTRUCT")
    console.log(`Key pressed: ${JSON.stringify(info)}`)
    //Key pressed: {"vkCode":65,"scanCode":30,"flags":128,"time":85736828,"dwExtraInfo":0}