import ffi from "@inigolabs/ffi-napi"; import ref from "@inigolabs/ref-napi"; import StructType from "ref-struct-di"; import { LONG, HWND, INT, BOOL, POINTER, LPARAM } from "./types"; const Struct = StructType(ref); const PROCESS_QUERY_LIMITED_INFORMATION = 0x1000; const lpdwordPtr = ref.refType(ref.types.ulong); const lpwstrPtr = ref.refType(ref.types.CString); const RECT = Struct({ left: LONG, top: LONG, right: LONG, bottom: LONG, }); const user32 = ffi.Library("user32", { GetWindowThreadProcessId: [INT, [HWND, lpdwordPtr]], GetWindowTextW: [INT, [HWND, POINTER, INT]], FindWindowW: [INT, [lpwstrPtr, lpwstrPtr]], EnumWindows: [BOOL, [POINTER, LPARAM]], IsWindowVisible: [BOOL, [HWND]], GetWindowRect: [BOOL, [HWND, POINTER]], }); const kernel32 = new ffi.Library("Kernel32", { OpenProcess: [INT, [INT, BOOL, INT]], CloseHandle: [BOOL, [INT]], QueryFullProcessImageNameW: [BOOL, [LONG, "uint32", lpwstrPtr, POINTER]], }); function decodeUnicodeBuffer(buffer) { return buffer .toString("ucs2") .replace(/[\u0000-\u0008,\u000A-\u001F,\u007F-\u00A0]+/g, ""); // NOSONAR } function getWindowData(hwnd) { const pidPtr = ref.alloc(lpdwordPtr); const threadPid = user32.GetWindowThreadProcessId(hwnd, pidPtr); const pid = pidPtr.readInt32LE(); const hProcess = kernel32.OpenProcess( PROCESS_QUERY_LIMITED_INFORMATION, false, pid ); const maxLength = 0x400; // 1024 const pPath = Buffer.alloc(maxLength); const pSize = ref.alloc(ref.types.uint32, maxLength); kernel32.QueryFullProcessImageNameW(hProcess, 0, pPath, pSize); const path = decodeUnicodeBuffer(pPath); kernel32.CloseHandle(hProcess); const pTitle = Buffer.alloc(512); user32.GetWindowTextW(hwnd, pTitle, 512); const title = decodeUnicodeBuffer(pTitle); return { title, hwnd, path, pid, threadPid }; } function findExactWindow(className, windowName) { const namePtr = windowName ? ref.allocCString(windowName, "ucs2") : null; const classNamePtr = className ? ref.allocCString(className, "ucs2") : null; return user32.FindWindowW(classNamePtr, namePtr); } function findWindow({ name, exeName }, callback) { let findedData; // use coz callback doesnt stop enumerating on return false (why?) let isIncludes; const windowsProc = ffi.Callback(INT, [HWND, LPARAM], (hwnd) => { if (user32.IsWindowVisible(hwnd)) { const data = getWindowData(hwnd); if (data.title && name) isIncludes = data.title.toLowerCase().includes(name.toLowerCase()); if (data.path && exeName) isIncludes = data.path.toLowerCase().includes(exeName.toLowerCase()); if (!findedData && isIncludes) { if (callback) callback(data); findedData = data; return 0; // must stop enumerate } } return 1; // continue enumerate }); user32.EnumWindows(windowsProc, 0); return findedData; } function getWindowRect(hwnd) { const rect = new RECT(); const result = user32.GetWindowRect(hwnd, rect.ref()); return result ? rect : false; } export { getWindowData, findWindow, findExactWindow, getWindowRect };