Skip to content

qulingoo/win-proxy

Repository files navigation

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)

获取与指定窗口有特定关系的窗口。cmdGW_* 常量(如 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)

获取祖先窗口句柄。flagsGA_* 常量(GA_PARENT = 1, GA_ROOT = 2, GA_ROOTOWNER = 3)。

getWindowThreadProcessId(hwnd)

获取窗口所属线程和进程 ID,返回 ThreadProcessId { threadId, processId }

getWindowLongPtr(hwnd, index)

获取窗口长指针值。indexGWLP_* 常量。

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)

控制窗口显示状态。cmdSW_* 常量:

  • 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)

垂直滚轮。正值向上滚动,负值向下滚动。通常步进为 120WHEEL_DELTA)。

mouseScroll(120);   // 向上滚一格
mouseScroll(-120);  // 向下滚一格
mouseScroll(360);   // 向上滚三格

mouseScrollHorizontal(delta)

水平滚轮。正值向右,负值向左。


键盘 API

按键操作

vk 参数为虚拟键码(Virtual Key Code),常见值:

  • 0x08 Backspace, 0x0D Enter, 0x1B Escape
  • 0x10 Shift, 0x11 Ctrl, 0x12 Alt
  • 0x20 Space, 0x2D Insert, 0x2E Delete
  • 0x410x5A AZ, 0x300x39 09
  • VK_F1VK_F12: 0x700x7B

keyDown(vk)

按下按键。

keyUp(vk)

释放按键。

keyPress(vk)

按下并释放按键(单击)。

keyPress(0x0D); // 按下 Enter
keyPress(0x1B); // 按下 Escape

keyCombo(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)

虚拟键码↔扫描码↔字符映射。mapType0 = 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)

等待进程退出。timeoutMs0xFFFFFFFF 表示无限等待。

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

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors