import ffi from "@inigolabs/ffi-napi"; import { app, ipcMain } from "electron"; import { Worker } from "worker_threads"; import { LPARAM, WPARAM, BOOL, DWORD, UINT } from "../utils/winapi/types"; const isDev = process.env.NODE_ENV === "development"; const WM_QUIT = 0x0012; let enumWindows = []; let threadId; const user32 = ffi.Library("user32", { PostThreadMessageA: [BOOL, [DWORD, UINT, WPARAM, LPARAM]], }); export default function initWinEventChannel(mainWindow) { // Worker threads cant access files is app.asar, access unpacked worker js files in app.asar.unpacked const wineventWorker = new Worker( `${app .getAppPath() .replace("app.asar", "app.asar.unpacked")}/dist/workers/winEventHook.js` ); wineventWorker.on("message", ({ event, data }) => { if (event === "enum-foreground") { enumWindows.push(data); return; } if (event === "set-threadId") { threadId = data; return; } mainWindow.webContents.send("winevent", { event, data }); }); // Because worker listen os event loop - its impossible get reply from listener it, so used main cached reply in [enumWindows] ipcMain.on("winevent", (_event, arg) => { if (arg === "enum-foreground") { enumWindows.forEach((data) => mainWindow.webContents.send("winevent", { event: "foreground", data, }) ); if (!isDev) enumWindows = []; } }); // Terminate worker on app exit, prevent long awaiting on app.relaunch ipcMain.on("main", ({ event }) => { if (event === "app-exit" && threadId) { // send QUIT message to winEventHook event loop. This prevents it from hanging on relaunch user32.PostThreadMessageA(threadId, WM_QUIT, 0, 0); wineventWorker.terminate(); } }); }