Skip to content

Releases: DC1024/swap-monitors

v1.1.5 · 多屏窗口互换

Choose a tag to compare

@DC1024 DC1024 released this 11 Oct 03:52

热修复:补齐了一个藏了好几版的老 bug —— 界面上好些弹窗(提示、确认、警告)从
v1.0 起其实从来没弹出来过,因为 messagebox 压根没被 import。你之前遇到的
「点了没反应」大多就是它。

修掉的问题

  • 界面上所有弹窗从来不出现(v1.0 就有的坑,这次一并修好)

    程序里到处在用 messagebox(提权确认、开机自启失败提醒、互换失败警告、
    托盘溢出提示……),但从第一版起 from tkinter import messagebox 这一行就缺着。
    于是一到要弹窗的地方就抛 NameError,被 Tk 悄悄吞掉 —— 表现就是**「点了没反应」**,
    而且那行「记住管理员模式」勾选后的引导也会走到一半就断、勾选框还会弹回去。

    现在是这么处理并锁死的:

    1. 补上缺失的 import,所有弹窗真正能用;
    2. 「托盘图标一次性提示」改成先展示成功、再记账 —— 以前先写标记再弹窗,
      一旦弹窗抛异常,这条提示就被永久吞掉,用户这辈子都看不到;
    3. 加了一个 tests/test_static.py 静态自检:用标准库 symtable 扫一遍,
      凡是「引用了但没定义」的全局名直接报出来(就是专门抓这种少写一行 import 的 bug),
      并且自带反向用例验证检测器自己不是假绿。
  • --diag 输出修正(沿用 v1.1.4 的说明不变)

其它

  • 上版本说的「记住管理员模式 / 无标题窗口也能搬 / 托盘图标不会丢」都还在;
    如果你之前勾选「记住管理员模式」没成功(被上面那个 bug 卡住了),
    这次重新勾一次即可,会正常弹 UAC 引导提权。

排错

出问题时跑一下自检,把输出贴出来就够定位:

SwapMonitors.exe --diag

会打印版本、exe 路径、是否管理员、计划任务是否存在/是否最高权限/是否指向当前 exe、
开机自启项是否存在、单实例互斥体的名字与抢锁结果等。

与旧版本兼容

设置文件沿用,热键、排除标题、排列映射都不会丢。

校验值

下载后可以核对一下哈希,确认文件完整、未被篡改:

SHA256  SwapMonitors.exe
824989cf7fd3017f614f562453145ca859b4c0daaeaa1890a83a83dcaa0770c7

命令行里核对:

certutil -hashfile SwapMonitors.exe SHA256

这个哈希是本地打包产物算出来的,与上传到 Release 的是同一个文件。

v1.1.4 · 多屏窗口互换

Choose a tag to compare

@DC1024 DC1024 released this 10 Oct 20:21

修掉「重启后托盘没图标」「无标题窗口搬不动」两个坑,并加上记住管理员模式。

新功能

  • 记住管理员模式(不再每次弹 UAC)

    勾上这个开关后,程序会注册一条名为 SwapMonitors-Elevated 的计划任务,
    触发条件是「该用户登录时」,运行级别是 最高权限。由任务计划服务代为启动,
    全程不弹 UAC —— 这是它和「给 exe 加『以管理员身份运行』兼容性标记」的关键区别,
    后者每次启动照样要弹。

    之后无论开机自启还是手动双击 exe,都会自动切到管理员实例上运行;
    取消勾选会把任务删掉、恢复普通权限。
    首次勾选需要提权一次(那时还没有任务,创建任务本身就得有管理员权限),
    程序会问一句然后重启一次,这一次之后就不会再问了。

  • 双击 exe 会把已运行的窗口叫到前台

    程序用命名互斥体保证只有一个实例。已经有一份在跑时再双击 exe,
    不会冒出第二个托盘图标,而是把已有窗口提到前台。
    隔离的数据目录让「管理员实例 ↔ 普通实例」也能这样通信。

修掉的问题

  • 无标题窗口搬不动(DeepSeek Harness / VS Code / Discord 这类 Electron 应用)

    Electron 应用的主窗口经常完全不设标题,而旧版本按「没有标题就跳过」处理,
    于是这些窗口在列表里根本不出现,自然永远搬不动 —— 表现就是
    「开了管理员权限也搬不动 DSH 窗口」。

    现在改成:标题为空时,要求它有标题栏(WS_CAPTION)且尺寸不小于 160×120
    才当窗口看待。这样既能收下 Electron 主窗,又不会把输入法候选框、
    隐形辅助窗这类小尺寸伪窗口一起搬走。列表里这类窗口显示为 [无标题] 进程名。

  • 重启后托盘里没有图标

    三种情况都处理了:

    1. 注册失败就重试(最多 6 轮,每轮 0.4 秒)—— 开机太早时资源管理器还没就绪;
    2. 收到资源管理器的 TaskbarCreated 广播就重新挂一遍
      (资源管理器重启/开机首启都会广播,旧版本不处理,图标丢了就永久丢了);
    3. 每 20 秒心跳确认一次图标还在,被系统悄悄清掉就自动重挂。
  • 托盘图标被 Win11 收进「^ 隐藏的图标」

    这是新装图标在 Win11 上的默认行为,不是程序没启动。
    程序启动时会比较图标矩形和溢出区面板的位置来判断,是的话弹一次说明,
    告诉你怎么把它拖到任务栏上固定(拖一次就够,Windows 会记住)。

    为什么不能全自动?注册表里确实有 IsPromoted 标记,程序也会写上,
    但实测(Win11 + 自动隐藏任务栏)它不会立刻生效 —— 资源管理器在图标
    创建时读的是自己内存里的缓存,而且图标被移除时还会把缓存状态写回去。
    所以「让你拖一下」是唯一确实管用的办法。

  • 任务栏自动隐藏时,命令行输出中文/特殊字符会崩

    --list / --json 在控制台编码为 GBK 时,一旦窗口标题里有 GBK 编不出来的字符
    (比如 Edge 标签页标题里的零宽空格 U+200B)就会抛 UnicodeEncodeError 直接退出 ——
    表现是「exe 一闪而过」。现在给控制台加了 errors="replace",
    机器可读的 --json 输出改为纯 ASCII 转义,从根上免疫编码问题。

排错

出问题时跑一下自检,把输出贴出来就够定位:

SwapMonitors.exe --diag

会打印版本、exe 路径、是否管理员、计划任务是否存在/是否最高权限/是否指向当前 exe、
开机自启项是否存在、单实例互斥体的名字与抢锁结果等。

与旧版本兼容

设置文件沿用,热键、排除标题、排列映射都不会丢。

如果你之前用「以管理员身份重启」手工提权过,这次需要在界面上把
「记住管理员模式」勾一次
(会弹一次 UAC),之后才会真正记住。

校验值

下载后可以核对一下哈希,确认文件完整、未被篡改:

SHA256  SwapMonitors.exe
062b2b9d24da9271ac470d5898c9cd506900bf0e93a7424c8cfcfb7f731c93db

命令行里核对:

certutil -hashfile SwapMonitors.exe SHA256

这个哈希是本地打包产物算出来的,与上传到 Release 的是同一个文件。

v1.1.3 · 多屏窗口互换

Choose a tag to compare

@DC1024 DC1024 released this 07 Oct 16:48

开机自启现在会跟着 exe 走,换位置不用重新设置。

改进

  • 自启项自愈(换位置不再需要手动关再开)
    以前启动文件夹里那个 .cmd 写死 exe 的绝对路径,程序一旦换目录、换盘,
    它就指向了不存在的文件:开机时要么弹一个黑窗报错,要么干脆不起来,
    必须手动把「开机自动启动」关掉再打开一次才会刷新。

    现在每次启动都会比对一次:启动项丢了、或者里面记的不是当前这个 exe,
    就静默重写一遍。把程序挪到任何位置,只要正常跑一次就自动跟上。
    (8.3 短路径 / 长路径、大小写差异都能正确识别,不会无谓重写。)

  • 启动项更耐错:.cmd 里加了一行存在性检查 —— 万一 exe 真的被删了,
    开机时静默跳过,不再弹黑窗吓人。

升级说明

从 v1.0 / v1.1.x 升级不会丢设置:配置仍在 %APPDATA%\SwapMonitors\config.json。

环境要求

  • Windows 10 / 11(64 位)

已知限制

  • 仅支持 Windows:实现全部基于 Win32 API。
  • 以管理员身份运行的程序需要提权后才能搬运(Windows 的 UIPI 限制,无法绕过)。
  • 躺在其它虚拟桌面上的窗口会被自动跳过(按 DWM cloaked 状态判定)。
  • 部分 UWP 应用由 ApplicationFrameHost 托管,位置由系统管理,可能搬不动。
  • 屏幕分辨率不同(需要缩放)时会有 1px 级别的误差,数学上无法避免;
    同分辨率的屏之间是纯整数平移,逐像素精确。
  • 混合 DPI(两块屏缩放百分比不同)没有在真机上验证过,欢迎反馈实测结果。

校验值

下载后可以核对一下哈希,确认文件完整、未被篡改:

SHA256  SwapMonitors.exe
b5c7a535f3f85089410f3f973d33bb4b852379a8c801ea90ab64e601e34d3884

命令行里核对:

certutil -hashfile SwapMonitors.exe SHA256

这个哈希是本地打包产物算出来的,与上传到 Release 的是同一个文件。

v1.1.2 · 多屏窗口互换

Choose a tag to compare

@DC1024 DC1024 released this 07 Oct 16:39

修复「已经以管理员身份运行,界面却一直显示普通用户」的问题。

修掉的问题

  • 权限标签永远显示「普通用户」:即使通过「以管理员身份重启」成功提权
    (UAC 已批、进程确实是管理员),界面右上角仍显示
    「权限:普通用户(管理员窗口搬不动)」。

    根因不在权限本身,而在检测函数的一个 ctypes 签名疏漏:GetCurrentProcess()
    返回的 -1 伪句柄经 HANDLE(c_void_p)转成 Python 里的 2⁶⁴−1,
    而 OpenProcessToken 没有显式设置参数签名,ctypes 按默认的 c_int 传参时
    直接 OverflowError——这个异常又被兜底的 except 吞掉,于是
    is_elevated() 在任何进程里都返回 False,包括提权后的自己。

  • 连带修掉的后果:提权后的实例再点「以管理员身份重启」不会正确拦截,
    会再弹一次 UAC;搬不动管理员窗口时的「是否提权」询问也会照常出现。

    修复:给 OpenProcessToken / GetTokenInformation / GetCurrentProcess
    显式设置 argtypes/restype。并用回归测试锁住这个签名(伪句柄必须能原样
    传进 OpenProcessToken)。

升级说明

从 v1.0 / v1.1.x 升级不会丢设置:配置仍在 %APPDATA%\SwapMonitors\config.json。

环境要求

  • Windows 10 / 11(64 位)

已知限制

  • 仅支持 Windows:实现全部基于 Win32 API。
  • 以管理员身份运行的程序需要提权后才能搬运(Windows 的 UIPI 限制,无法绕过)。
  • 躺在其它虚拟桌面上的窗口会被自动跳过(按 DWM cloaked 状态判定)。
  • 部分 UWP 应用由 ApplicationFrameHost 托管,位置由系统管理,可能搬不动。
  • 屏幕分辨率不同(需要缩放)时会有 1px 级别的误差,数学上无法避免;
    同分辨率的屏之间是纯整数平移,逐像素精确。
  • 混合 DPI(两块屏缩放百分比不同)没有在真机上验证过,欢迎反馈实测结果。

校验值

下载后可以核对一下哈希,确认文件完整、未被篡改:

SHA256  SwapMonitors.exe
5a30201811f44135d79eb68c0aabb74c472246c4fb3f33eb5d5df396eff9b0b5

命令行里核对:

certutil -hashfile SwapMonitors.exe SHA256

这个哈希是本地打包产物算出来的,与上传到 Release 的是同一个文件。

v1.1.1 · 多屏窗口互换

Choose a tag to compare

@DC1024 DC1024 released this 07 Oct 16:14

修复 v1.1.0 里「以管理员身份重启」会失败的问题。

修掉的问题

  • 点「以管理员身份重启」后弹 Security validation failure: invalid originating onefile parent process (PID not found)!,然后新旧两个实例全都退出了。

    这是 PyInstaller 6.22 起的 onefile 安全校验(#9492/#9520)和「应用内提权重启」
    这个场景相性不合:UAC 提权出来的新进程会继承旧实例的 _PYI_* 内部环境变量,
    于是被安全校验当成了 onefile 子进程,去核对「原始父进程」——而旧实例的
    bootloader 已经退出了,核对不到 PID,就直接拒绝启动。

    修复:提权重启前把 _PYI_* 变量摘干净,并按官方文档设置
    PYINSTALLER_RESET_ENVIRONMENT=1(告诉 bootloader「这是一个全新的独立实例,
    不是 worker 子进程」),ShellExecuteW 把环境块复制走之后再原样还原。
    双保险,两个机制各管各的版本区间。

  • 顺带:提权成功后旧实例的退出时序更稳(先释放热键、再拉新实例、最后退出,
    避免新实例注册热键时撞上还没退干净的旧实例)。

升级说明

从 v1.0 / v1.1.0 升级不会丢设置:配置仍在 %APPDATA%\SwapMonitors\config.json。

环境要求

  • Windows 10 / 11(64 位)

已知限制

  • 仅支持 Windows:实现全部基于 Win32 API。
  • 以管理员身份运行的程序需要提权后才能搬运(Windows 的 UIPI 限制,无法绕过)。
  • 躺在其它虚拟桌面上的窗口会被自动跳过(按 DWM cloaked 状态判定)。
  • 部分 UWP 应用由 ApplicationFrameHost 托管,位置由系统管理,可能搬不动。
  • 屏幕分辨率不同(需要缩放)时会有 1px 级别的误差,数学上无法避免;
    同分辨率的屏之间是纯整数平移,逐像素精确。
  • 混合 DPI(两块屏缩放百分比不同)没有在真机上验证过,欢迎反馈实测结果。

校验值

下载后可以核对一下哈希,确认文件完整、未被篡改:

SHA256  SwapMonitors.exe
bfc36e2ae5f3c9cf39d8b46a1dacccb055f6b5b19b274b39e686da053606c747

命令行里核对:

certutil -hashfile SwapMonitors.exe SHA256

这个哈希是本地打包产物算出来的,与上传到 Release 的是同一个文件。

v1.1.0 · 多屏窗口互换

Choose a tag to compare

@DC1024 DC1024 released this 06 Oct 19:15

支持三块以上屏幕,并修掉两个交互上的坑。

新功能

  • 三块屏以上也能用 —— 每块屏都有独立的「送往」下拉,可以环形轮转(1→2→3→1)、
    任意指定配对,或者只换其中两块。没被指定去向的屏,上面的窗口一个都不动。
  • 三个预设 —— 环形轮转 / 两两互换 / 全部不动,一键配好,不用一个个改下拉。
  • 命令行 —— 新增 --rotate 1,2,3(环形轮转)和 --permute "1>2,2>3,3>1"(任意排列)。
    不合法的排列(屏号越界、送往自己、两块屏挤到同一块目标屏)会被直接拒掉。
  • 互换完成提示可以关掉 —— 新增开关,不想被托盘气泡打扰就关掉,互换静默完成。
  • 显示名改为「多屏窗口互换」。

修掉的问题

  • 窗口底部的选项被裁掉:少显示约 100px 的内容(「互换完成后弹出提示」「开机自动启动」
    等选项看不见)。原因是窗口尺寸在内容长高之前就量好了。
  • 开机自启会闪一下窗口:现在只有「开机自动启动」那条路会静默进托盘,
    手动双击一定显示界面。以前手动启动也会直接躲进托盘,会让人以为程序没启动。
  • 缩放方式改成按「屏对」分别判断:三块屏混排时,1→2 可以走纯平移、2→3 走等比缩放,
    不再是全局一个开关。

最大化的窗口能搬走吗?

能。在 Windows 上你既拖不动也挪不动一个最大化窗口 —— 给它 SetWindowPos 会被系统立刻
按「贴满本屏」重排、弹回原来那块屏。可行路径只有一条:

取消最大化  →  把「还原位置」改到目标屏  →  在新屏重新最大化

走的就是这条,并且在之后会验证窗口中心确实落在目标屏上,失败会重试一次。
代价是互换瞬间最大化窗口会闪一下。

升级说明

从 v1.0 升级不会丢设置:配置仍然存在 %APPDATA%\SwapMonitors\config.json。
老版本里那个「启动时直接收进托盘」选项已移除(它的行为并入了「开机自动启动」)。

环境要求

  • Windows 10 / 11(64 位)

已知限制

  • 仅支持 Windows:实现全部基于 Win32 API。
  • 以管理员身份运行的程序需要提权后才能搬运(Windows 的 UIPI 限制,无法绕过)。
  • 躺在其它虚拟桌面上的窗口会被自动跳过(按 DWM cloaked 状态判定)。
  • 部分 UWP 应用由 ApplicationFrameHost 托管,位置由系统管理,可能搬不动。
  • 屏幕分辨率不同(需要缩放)时会有 1px 级别的误差,数学上无法避免;
    同分辨率的屏之间是纯整数平移,逐像素精确。
  • 混合 DPI(两块屏缩放百分比不同)没有在真机上验证过,欢迎反馈实测结果。

校验值

下载后可以核对一下哈希,确认文件完整、未被篡改:

SHA256  SwapMonitors.exe
d0b039879f2c43d639fcf65f08e8cf0e379ab7a9ae0d4a6ac6791e8ace7a4917

命令行里核对:

certutil -hashfile SwapMonitors.exe SHA256

这个哈希是本地打包产物算出来的,与上传到 Release 的是同一个文件。

v1.0.0 · 双屏窗口互换

Choose a tag to compare

@DC1024 DC1024 released this 06 Oct 18:31

首个正式版本。

它是什么

按一下全局热键(默认 Ctrl+Alt+S),把两块显示器上的所有窗口整体互换,
贴附好的布局原样保留。

亮点

  • 布局真的不乱 —— 只改窗口的矩形位置、不动窗口状态。最大化过去还是最大化,
    左右吸附过去还是贴着边,同尺寸屏下逐像素可逆。
  • 常驻托盘 —— 点 ✕ 或最小化都收进系统托盘,不占任务栏;热键继续有效。
  • 能搬动管理员窗口 —— 内置「以管理员身份重启」;遇到搬不动的窗口会主动提示提权。
  • 分辨率无关 —— 同尺寸纯平移(完全可逆);尺寸不同自动等比缩放并夹进工作区。
  • 零依赖 —— 单文件 exe 自带 Python 运行时;源码只用标准库(ctypes + tkinter)。

下载

SwapMonitors.exe —— 免安装,双击即用,无需安装 Python 或任何依赖。

首次运行可能提示「已保护你的电脑」,这是个人开发者未购买代码签名证书时的正常提示。
点「更多信息」→「仍要运行」即可。程序不联网、不上传任何数据。

环境要求

  • Windows 10 / 11(64 位)

已知限制

  • 仅支持 Windows:实现全部基于 Win32 API。
  • 以管理员身份运行的程序需要提权后才能搬运(Windows 的 UIPI 限制,无法绕过)。
  • 躺在其它虚拟桌面上的窗口会被自动跳过。
  • 部分 UWP 应用由 ApplicationFrameHost 托管,位置由系统管理,可能搬不动。
  • 一次只处理两块屏;三屏以上可用 --monitors 1,3 指定本次要动的两块。

校验值

下载后可以核对一下哈希,确认文件完整、未被篡改:

SHA256   SwapMonitors.exe
9fb469bc7873a17da23a35cddd4ef6a440a47f1b1c530f863aa0a9a4125ff5c0

命令行里核对:

certutil -hashfile SwapMonitors.exe SHA256