如何用AutoHotkey实现两个浏览器窗口的操作同步?
问题描述
需要同步两个火狐浏览器中打开的Google Earth窗口操作(包括鼠标移动、缩放、拖拽、键盘输入等),现有AutoHotkey v2脚本中:
- F8可正常检测并显示目标窗口详情
- F9用于切换同步控制的开启/关闭状态
但核心同步功能失效,输入事件无法镜像到第二个窗口。
现有脚本
#Requires AutoHotkey v2.0 #SingleInstance Force SetTitleMatchMode 2 ; This makes it match "Google Earth" anywhere in title global sync := false global geTitle := "Google Earth" global debugMode := true ; NEW: Enable debug messages ; Helper function for debugging DebugMsg(msg) { global debugMode if debugMode { ToolTip(msg) SetTimer(() => ToolTip(), -3000) } } ; For debugging F8:: { winList := WinGetList("Google Earth ahk_exe firefox.exe") MsgBox("Found " winList.Length " windows") ; Show details of each window for id in winList { title := WinGetTitle("ahk_id " id) MsgBox("Window ID: " id "`nTitle: " title) } } F9:: { global sync sync := !sync ToolTip("Google Earth SYNC " . (sync ? "ON" : "OFF")) SetTimer(() => ToolTip(), -800) } F12::ExitApp() ; Using your window title variable - THIS IS THE FIX #HotIf sync && WinActive("Google Earth ahk_exe firefox.exe") ~LButton::MirrorKey("LButton") ~RButton::MirrorKey("RButton") ~MButton::MirrorKey("MButton") ~WheelUp::MirrorKey("WheelUp") ~WheelDown::MirrorKey("WheelDown") ~a::MirrorKey("a") ~d::MirrorKey("d") ~w::MirrorKey("w") ~s::MirrorKey("s") ~q::MirrorKey("q") ~e::MirrorKey("e") ~Left::MirrorKey("Left") ~Right::MirrorKey("Right") ~Up::MirrorKey("Up") ~Down::MirrorKey("Down") ~NumpadAdd::MirrorKey("NumpadAdd") ~NumpadSub::MirrorKey("NumpadSub") #HotIf MirrorKey(key) { activeID := WinExist("A") winList := WinGetList("Google Earth ahk_exe firefox.exe") ; Using your title variable if winList.Length < 2 return for thisID in winList { if (thisID != activeID) { ControlSend "{Blind}{" key "}","MozillaCompositorWindowClass1" , "ahk_id " thisID } } }
调试建议
- 验证控件类名正确性:火狐浏览器的控件类名可能并非
MozillaCompositorWindowClass1,使用AutoHotkey自带的Window Spy工具,定位目标Google Earth窗口的实际控件类名,替换到ControlSend的第二个参数中;也可以直接留空该参数,让ControlSend自动选择窗口的顶层控件。 - 增强调试日志:在
MirrorKey函数的ControlSend行前后添加调试输出,确认代码是否执行到发送逻辑,以及目标窗口ID是否正确:DebugMsg("Attempting to send " key " to window " thisID) ControlSend "{Blind}{" key "}","目标控件类名" , "ahk_id " thisID DebugMsg("Sent " key " to window " thisID) - 测试无~前缀的热键:暂时去掉热键前的
~前缀,避免原事件干扰,确认同步功能生效后再恢复。 - 检查#HotIf条件匹配:使用
WinGetTitle("A")获取当前激活窗口的完整标题,确认是否包含"Google Earth";若标题有动态后缀,可调整SetTitleMatchMode 3(精确匹配)或修改WinActive条件为仅匹配进程:WinActive("ahk_exe firefox.exe")。 - 替换ControlSend为更可靠的发送方式:对于鼠标移动、拖拽这类非键盘事件,
ControlSend支持性较差,需单独处理:- 鼠标移动:获取当前窗口的相对鼠标坐标,转换为目标窗口的屏幕坐标后调用
MouseMove - 拖拽:结合
LButton的按下/释放事件,在目标窗口模拟按下、移动、释放的完整流程
- 鼠标移动:获取当前窗口的相对鼠标坐标,转换为目标窗口的屏幕坐标后调用
替代实现方案
以下是更完善的同步脚本,支持鼠标移动、拖拽、键盘、滚轮事件的同步:
#Requires AutoHotkey v2.0 #SingleInstance Force SetTitleMatchMode 2 global syncEnabled := false global targetWinTitle := "Google Earth ahk_exe firefox.exe" ; 调试开关 global debug := true DebugLog(msg) { if debug { ToolTip(msg) SetTimer(() => ToolTip(), -2000) } } ; 调试:列出目标窗口 F8:: { winList := WinGetList(targetWinTitle) MsgBox("找到 " winList.Length " 个目标窗口") for idx, id in winList { MsgBox("窗口ID: " id "`n标题: " WinGetTitle("ahk_id " id)) } } ; 切换同步状态 F9:: { global syncEnabled syncEnabled := !syncEnabled ToolTip("同步状态: " (syncEnabled ? "开启" : "关闭")) SetTimer(() => ToolTip(), -1000) } F12::ExitApp() ; 同步键盘事件 #HotIf syncEnabled && WinActive(targetWinTitle) ~*a::~*d::~*w::~*s::~*q::~*e:: ~*Left::~*Right::~*Up::~*Down:: ~*NumpadAdd::~*NumpadSub:: SyncKey(A_ThisHotkey) return ; 同步鼠标按键 ~*LButton::~*RButton::~*MButton:: SyncMouseButton(A_ThisHotkey) return ; 同步滚轮 ~*WheelUp::~*WheelDown:: SyncWheel(A_ThisHotkey) return #HotIf ; 同步鼠标移动(持续监听) SetTimer(SyncMouseMove, 10) SyncKey(key) { activeWin := WinExist("A") winList := WinGetList(targetWinTitle) if (winList.Length < 2) return key := StrReplace(key, "~*", "") for winId in winList { if (winId != activeWin) { ControlSend("{Blind}{" key "}",, "ahk_id " winId) DebugLog("发送按键 " key " 到窗口 " winId) } } } SyncMouseButton(btn) { activeWin := WinExist("A") winList := WinGetList(targetWinTitle) if (winList.Length < 2) return btn := StrReplace(btn, "~*", "") state := GetKeyState(btn, "P") for winId in winList { if (winId != activeWin) { if (state) { ControlSend("{" btn " down}",, "ahk_id " winId) DebugLog(btn " 按下 到窗口 " winId) } else { ControlSend("{" btn " up}",, "ahk_id " winId) DebugLog(btn " 释放 到窗口 " winId) } } } } SyncWheel(dir) { activeWin := WinExist("A") winList := WinGetList(targetWinTitle) if (winList.Length < 2) return dir := StrReplace(dir, "~*", "") for winId in winList { if (winId != activeWin) { ControlSend("{" dir "}",, "ahk_id " winId) DebugLog("滚轮 " dir " 到窗口 " winId) } } } SyncMouseMove() { if (!syncEnabled || !WinActive(targetWinTitle)) return activeWin := WinExist("A") winList := WinGetList(targetWinTitle) if (winList.Length < 2) return ; 获取当前窗口的相对鼠标坐标 MouseGetPos(&x, &y, &activeWin) WinGetClientPos(&cx, &cy, &cw, &ch, "ahk_id " activeWin) relX := x - cx relY := y - cy for winId in winList { if (winId != activeWin) { ; 转换为目标窗口的屏幕坐标 WinGetClientPos(&tx, &ty, &tw, &th, "ahk_id " winId) targetX := tx + relX targetY := ty + relY ; 移动鼠标到目标窗口对应位置 MouseMove(targetX, targetY, 0, "R") DebugLog("鼠标移动到窗口 " winId " 坐标: " targetX "," targetY) } } }
内容的提问来源于stack exchange,提问作者RaviS
相关产品推荐
相关产品推荐

