@qulingoo/win-proxy
v1.0.0
Published
Windows 自动化原生模块 — 窗口管理、截图、鼠标键盘模拟、进程控制、以图找图
Maintainers
Readme
win-proxy
基于 Rust + napi-rs 构建的 Windows 自动化原生 Node.js 模块。涵盖窗口管理、截图、鼠标键盘模拟、进程控制和计算机视觉(以图找图)等能力。
GitHub: https://github.com/qulingoo/win-proxy
安装与构建
npm install
npm run build # release 构建
npm run build:debug # debug 构建使用方式
const {
// 截图
captureScreen, captureRegion, captureWindow,
// 窗口
findWindow, enumWindows, getForegroundWindow,
// 输入
mouseClick, keyPress, keyTypeText,
// 进程
shellExecute, terminateProcessById,
// CV
findOnScreen, findAllOnScreen, loadImage, saveImage,
// 颜色
getColorOnScreen, findColorOnScreen,
} = require("./index.js");截图 API
captureScreen()
截取全屏,返回 ImageData。
const screen = captureScreen();
console.log(screen.width, screen.height, screen.buffer.length);captureRegion(x, y, width, height)
截取指定屏幕区域。
const region = captureRegion(100, 100, 400, 300);captureWindow(hwnd)
截取指定窗口。hwnd 为窗口句柄(i64)。
const hwnd = findWindow(null, "计算器");
const img = captureWindow(hwnd);返回类型 ImageData:
| 字段 | 类型 | 说明 |
|------|------|------|
| width | number | 图像宽度(像素) |
| height | number | 图像高度(像素) |
| buffer | Buffer | RGBA 像素数据 |
窗口 API
查找与枚举
findWindow(className?, windowName?)
根据类名或窗口标题查找顶层窗口,返回窗口句柄。未找到返回 0。
const hwnd = findWindow(null, "记事本");
const hwnd = findWindow("Notepad", null);findWindowEx(parent, afterChild, className?, windowName?)
在父窗口内查找子窗口。afterChild 为从前一个子窗口之后开始搜索,传 0 从头搜索。
const child = findWindowEx(parentHwnd, 0, "Edit", null);enumWindows()
枚举所有顶层窗口,返回句柄数组。
const handles = enumWindows();enumChildWindows(parent)
枚举指定窗口的所有子窗口。
const children = enumChildWindows(parentHwnd);windowFromPoint(x, y)
获取屏幕坐标处的窗口句柄。
getDesktopWindow()
获取桌面窗口句柄。
getForegroundWindow()
获取当前前台窗口句柄。
getTopWindow(hwnd)
获取指定窗口的 Z 序顶部子窗口。
getWindow(hwnd, cmd)
获取与指定窗口有特定关系的窗口。cmd 为 GW_* 常量(如 GW_OWNER = 4, GW_HWNDNEXT = 2)。
窗口信息
getWindowText(hwnd)
获取窗口标题文本。
setWindowText(hwnd, text)
设置窗口标题文本。
getClassName(hwnd)
获取窗口类名。
getWindowRect(hwnd)
获取窗口在屏幕上的矩形区域,返回 Rect { left, top, right, bottom }。
getClientRect(hwnd)
获取窗口客户区矩形,返回 Rect。
isWindow(hwnd)
判断句柄是否为有效窗口。
isWindowVisible(hwnd)
判断窗口是否可见。
isWindowEnabled(hwnd)
判断窗口是否启用(可交互)。
getParent(hwnd)
获取父窗口句柄。
getAncestor(hwnd, flags)
获取祖先窗口句柄。flags 为 GA_* 常量(GA_PARENT = 1, GA_ROOT = 2, GA_ROOTOWNER = 3)。
getWindowThreadProcessId(hwnd)
获取窗口所属线程和进程 ID,返回 ThreadProcessId { threadId, processId }。
getWindowLongPtr(hwnd, index)
获取窗口长指针值。index 为 GWLP_* 常量。
setWindowLongPtr(hwnd, index, value)
设置窗口长指针值。
getClassLongPtr(hwnd, index)
获取类长指针值。
getWindowPlacement(hwnd) / setWindowPlacement(hwnd, placement)
获取/设置窗口放置信息,返回/接受 WindowPlacement 对象。
const placement = getWindowPlacement(hwnd);
console.log(placement.showCmd); // 1=正常, 2=最小化, 3=最大化窗口操作
showWindow(hwnd, cmd)
控制窗口显示状态。cmd 为 SW_* 常量:
SW_HIDE = 0— 隐藏SW_SHOW = 5— 显示SW_MINIMIZE = 6— 最小化SW_MAXIMIZE = 3— 最大化SW_RESTORE = 9— 还原
setForegroundWindow(hwnd)
将窗口设为前台窗口。
moveWindow(hwnd, x, y, width, height, repaint)
移动并调整窗口大小。repaint 是否重绘。
setWindowPos(hwnd, after, x, y, width, height, flags)
设置窗口 Z 序和位置。after 可用 HWND_TOPMOST (0xFFFFFFFF) 等值。
closeWindow(hwnd)
最小化窗口(不是关闭,Win32 API 行为)。
bringWindowToTop(hwnd)
将窗口提到 Z 序顶部。
enableWindow(hwnd, enable)
启用/禁用窗口。
setFocus(hwnd) / getFocus()
设置/获取键盘焦点窗口。
updateWindow(hwnd)
强制窗口更新客户区。
窗口消息
sendMessage(hwnd, msg, wparam, lparam)
同步发送窗口消息,返回消息结果。
postMessage(hwnd, msg, wparam, lparam)
异步投递窗口消息到队列,返回是否成功。
鼠标 API
光标
getCursorPos()
获取光标位置,返回 Point { x, y }。
const pos = getCursorPos();
console.log(pos.x, pos.y);setCursorPos(x, y)
设置光标位置。
getCursorInfo()
获取光标详细信息,返回 CursorInfo { flags, showing, handle, x, y }。
showCursor(show)
显示/隐藏光标。true 显示,false 隐藏。
鼠标点击
按钮编号:0 = 左键,1 = 右键,2 = 中键。
mouseDown(button)
按下鼠标按键。
mouseUp(button)
释放鼠标按键。
mouseClick(button)
单击鼠标按键(按下+释放)。
mouseClick(0); // 左键单击mouseDoubleClick(button)
双击鼠标按键。
鼠标移动
mouseMoveTo(x, y)
绝对移动光标到屏幕坐标。
mouseMoveRelative(dx, dy)
相对当前光标位置移动。
滚轮
mouseScroll(delta)
垂直滚轮。正值向上滚动,负值向下滚动。通常步进为 120(WHEEL_DELTA)。
mouseScroll(120); // 向上滚一格
mouseScroll(-120); // 向下滚一格
mouseScroll(360); // 向上滚三格mouseScrollHorizontal(delta)
水平滚轮。正值向右,负值向左。
键盘 API
按键操作
vk 参数为虚拟键码(Virtual Key Code),常见值:
0x08Backspace,0x0DEnter,0x1BEscape0x10Shift,0x11Ctrl,0x12Alt0x20Space,0x2DInsert,0x2EDelete0x41~0x5AA~Z,0x30~0x390~9VK_F1~VK_F12:0x70~0x7B
keyDown(vk)
按下按键。
keyUp(vk)
释放按键。
keyPress(vk)
按下并释放按键(单击)。
keyPress(0x0D); // 按下 Enter
keyPress(0x1B); // 按下 EscapekeyCombo(modifiers, key)
组合键。先按住所有修饰键,再点击主键,最后释放所有修饰键。
keyCombo([0x11], 0x43); // Ctrl+C (复制)
keyCombo([0x11], 0x56); // Ctrl+V (粘贴)
keyCombo([0x11, 0x10], 0x1B); // Ctrl+Shift+Esc (任务管理器)
keyCombo([0x12], 0x09); // Alt+Tab文字输入
keyTypeChar(ch)
输入单个 Unicode 字符。
keyTypeText(text)
输入一段文字字符串。
keyTypeText("Hello, 世界!");键盘状态
getAsyncKeyState(vk)
异步获取按键状态(是否正在被按下)。
if (getAsyncKeyState(0x01)) { /* 鼠标左键正在按下 */ }getKeyState(vk)
获取按键状态。bit 15 = 是否按下,bit 0 = 是否切换(如 CapsLock)。
getKeyboardState()
获取全部 256 个虚拟键的状态,返回 Buffer(256 字节)。
键码映射
mapVirtualKey(code, mapType)
虚拟键码↔扫描码↔字符映射。mapType:0 = VK→扫描码, 1 = 扫描码→VK, 2 = VK→字符, 3 = 扫描码→VK(区分左右)。
vkKeyScan(ch)
字符→虚拟键码。低字节为 VK 码,高字节为修饰键状态(bit 0=Shift, bit 1=Ctrl, bit 2=Alt)。
进程 API
启动进程
shellExecute(verb?, file, args?, directory?, show)
通过 ShellExecuteEx 启动程序。返回 ProcessInfo { processId, threadId, handle }。
| 参数 | 说明 |
|------|------|
| verb | 操作:"open"(默认)、"runas"(管理员)、"print" 等,传 null 为默认 |
| file | 要打开的文件或程序路径 |
| args | 命令行参数 |
| directory | 工作目录 |
| show | 显示方式:1=正常, 0=隐藏, 3=最大化, 6=最小化 |
const proc = shellExecute(null, "notepad.exe", null, null, 1);
console.log(proc.processId, proc.handle);
// 以管理员权限运行
shellExecute("runas", "cmd.exe", null, null, 1);createProcess(application?, commandLine?, show)
通过 CreateProcess 启动程序,返回 ProcessInfo。
const proc = createProcess("C:\\Windows\\notepad.exe", null, 1);终止进程
terminateProcessById(pid, exitCode)
通过进程 ID 终止进程。
terminateProcessById(1234, 0);terminateProcess(handle, exitCode)
通过进程句柄终止进程。
closeHandle(handle)
关闭句柄(不会终止进程)。
进程信息
isProcessActive(handle)
检查进程是否仍在运行。
if (isProcessActive(proc.handle)) {
console.log("进程仍在运行");
}getProcessId(handle)
从进程句柄获取 PID。
waitForProcess(handle, timeoutMs)
等待进程退出。timeoutMs 传 0xFFFFFFFF 表示无限等待。
waitForInputIdle(handle, timeoutMs)
等待进程初始化完成(用于启动后等待窗口就绪)。
进程列表
enumProcesses()
枚举所有运行中的进程,返回 ProcessEntry[]。
const procs = enumProcesses();
for (const p of procs) {
console.log(`PID: ${p.processId} 父PID: ${p.parentProcessId} 线程: ${p.threadCount} ${p.exeFile}`);
}findProcesses(name)
按进程名模糊搜索(不区分大小写)。
const notepads = findProcesses("notepad");killProcessesByName(name, exitCode)
按名称批量终止进程,返回被终止的 PID 列表。
const killed = killProcessesByName("notepad", 0);
console.log(`已终止 ${killed.length} 个进程`);计算机视觉 (CV) API
模板匹配(以图找图)
基于 NCC(归一化互相关)算法 + rayon 多线程两阶段搜索。1920x1080 全屏搜索约 400ms。
findImage(source, srcW, srcH, template, tplW, tplH, options?)
在源图像 buffer 中搜索模板,返回第一个匹配(或 null)。
const src = loadImage("screenshot.png");
const tpl = loadImage("target.png");
const match = findImage(src.buffer, src.width, src.height, tpl.buffer, tpl.width, tpl.height, {
threshold: 0.9,
});
if (match) {
console.log(`找到: (${match.x}, ${match.y}) 匹配度: ${(match.score * 100).toFixed(1)}%`);
}findAllImages(source, srcW, srcH, template, tplW, tplH, options?)
查找所有匹配位置,返回 MatchResult[]。
findOnScreen(template, tplW, tplH, options?)
直接在屏幕上搜索模板(自动截图),返回第一个匹配。
const tpl = loadImage("button.png");
const match = findOnScreen(tpl.buffer, tpl.width, tpl.height, {
threshold: 0.85,
});
if (match) {
mouseMoveTo(match.x + match.width / 2, match.y + match.height / 2);
mouseClick(0);
}findAllOnScreen(template, tplW, tplH, options?)
在屏幕上搜索所有匹配位置。
MatchOptions 参数:
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| threshold | number | 0.8 | 匹配阈值 (0~1),越高越严格 |
| maxResults | number | 10 | 最大返回数量(仅 findAll* 有效) |
| step | number | 自动 | 搜索步长(内部自动两阶段优化) |
| regionX | number | 0 | 搜索区域 X 起点 |
| regionY | number | 0 | 搜索区域 Y 起点 |
| regionWidth | number | 全屏 | 搜索区域宽度 |
| regionHeight | number | 全屏 | 搜索区域高度 |
MatchResult 返回值:
| 字段 | 类型 | 说明 |
|------|------|------|
| x | number | 匹配位置 X(左上角) |
| y | number | 匹配位置 Y(左上角) |
| width | number | 模板宽度 |
| height | number | 模板高度 |
| score | number | 匹配分数 (0~1) |
颜色操作
getColorAt(image, width, x, y)
获取图像 buffer 中指定坐标的颜色,返回 Color { r, g, b, a }。
const screen = captureScreen();
const color = getColorAt(screen.buffer, screen.width, 100, 200);
console.log(`rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`);getColorOnScreen(x, y)
直接获取屏幕上指定坐标的颜色(自动截图 1x1 区域)。
findColor(image, width, height, r, g, b, tolerance?)
在图像 buffer 中查找指定颜色,返回 Point { x, y } 或 null。
const screen = captureScreen();
const pt = findColor(screen.buffer, screen.width, screen.height, 255, 0, 0, 30);
if (pt) console.log(`找到红色: (${pt.x}, ${pt.y})`);findColorOnScreen(r, g, b, tolerance?)
直接在屏幕上查找指定颜色。tolerance 默认 10,值越大容差越大。
图片 I/O
loadImage(path)
从文件加载图片(支持 PNG、JPEG、BMP 等),返回 LoadedImage { width, height, buffer }。
const img = loadImage("template.png");saveImage(path, buffer, width, height)
将 RGBA buffer 保存为图片文件(根据扩展名决定格式)。
const screen = captureScreen();
saveImage("screenshot.png", screen.buffer, screen.width, screen.height);类型汇总
interface ImageData { width: number; height: number; buffer: Buffer }
interface LoadedImage { width: number; height: number; buffer: Buffer }
interface Rect { left: number; top: number; right: number; bottom: number }
interface Point { x: number; y: number }
interface WindowPlacement {
flags: number; showCmd: number;
minPosition: Point; maxPosition: Point; normalPosition: Rect;
}
interface ThreadProcessId { threadId: number; processId: number }
interface CursorInfo { flags: number; showing: boolean; handle: number; x: number; y: number }
interface ProcessInfo { processId: number; threadId: number; handle: number }
interface ProcessEntry { processId: number; parentProcessId: number; threadCount: number; exeFile: string }
interface MatchResult { x: number; y: number; width: number; height: number; score: number }
interface MatchOptions {
threshold?: number; step?: number; maxResults?: number;
regionX?: number; regionY?: number; regionWidth?: number; regionHeight?: number;
}
interface Color { r: number; g: number; b: number; a: number }反馈
遇到 Bug、有功能建议或使用疑问,欢迎提交 Issue,会尽快回复。
许可证
ISC
