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