Call Windows SetWinEventHook from nodejs

2019-08-27 19:27发布

问题:

I'm trying to call SetWinEventHook as described here for C# but from nodejs.

I'm using ffi-napi to bind to the function. Here's my code so far:

const ffi = require("ffi-napi")

const user32 = ffi.Library("user32", {
    SetWinEventHook: ["int", ["int", "int", "pointer", "pointer", "int", "int", "int"]]
})

const pfnWinEventProc = ffi.Callback("void", ["pointer", "int", "pointer", "long", "long", "int", "int"],
    function (hWinEventHook, event, hwnd, idObject, idChild, idEventThread, dwmsEventTime) {
        console.log("Callback!")
        console.log(arguments)
    })

const EVENT_SYSTEM_FOREGROUND = 3
const WINEVENT_OUTOFCONTEXT = 0
const WINEVENT_SKPIOWNPROCESS = 2

user32.SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, null, pfnWinEventProc,
    0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKPIOWNPROCESS)

setInterval(function () {
    // keep the script alive
}, 500)

process.on("exit", function () {
    console.log("Exiting")
    pfnWinEventProc
})

The problem is simply my callback is not being called. It should be called whenever the focused window is changed.

I'm not getting any errors either so I'm quite lost as to what I'm doing wrong here.

The code is here as well if you want to check it out.

回答1:

I finally got it working using a combination of the cluster module (if you don't want to block the event loop with the message loop) and peeking at the Windows part of the active-win package.

The full code is here.

const ffi = require("ffi-napi")
const cluster = require("cluster")
const ref = require("ref")
const wchar = require("ref-wchar")

if (cluster.isMaster) {
    console.log("Main code here...")
    cluster.fork()
} else {
    const msgType = ref.types.void
    const msgPtr = ref.refType(msgType)
    const EVENT_SYSTEM_FOREGROUND = 3
    const WINEVENT_OUTOFCONTEXT = 0
    const WINEVENT_SKPIOWNPROCESS = 2

    const user32 = ffi.Library("user32", {
        SetWinEventHook: ["int", ["int", "int", "pointer", "pointer", "int", "int", "int"]],
        GetWindowTextW: ["int", ["pointer", "pointer", "int"]],
        GetWindowTextLengthW: ["int", ["pointer"]],
        GetMessageA: ["bool", [msgPtr, "int", "uint", "uint"]]
    })

    function getMessage() {
        return user32.GetMessageA(ref.alloc(msgPtr), null, 0, 0)
    }

    const pfnWinEventProc = ffi.Callback("void", ["pointer", "int", "pointer", "long", "long", "int", "int"],
        function (hWinEventHook, event, hwnd, idObject, idChild, idEventThread, dwmsEventTime) {
            const windowTitleLength = user32.GetWindowTextLengthW(hwnd)
            const bufferSize = windowTitleLength * 2 + 4
            const titleBuffer = Buffer.alloc(bufferSize)
            user32.GetWindowTextW(hwnd, titleBuffer, bufferSize)
            const titleText = ref.reinterpretUntilZeros(titleBuffer, wchar.size)
            const finallyWindowTitle = wchar.toString(titleText)
            console.log(finallyWindowTitle)
        }
    )

    user32.SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, null, pfnWinEventProc,
        0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKPIOWNPROCESS)

    let res = getMessage()
    while(res != 0) {
        switch (res) {
            case -1:
                console.log("Invalid GetMessageA arguments or something!");
                break
            default:
                console.log("Got a message!")
        }
        res = getMessage()
    }
}