2.1.5：修「Windows 7 装完报 dll 缺失」

用户报：学校的电脑是 Windows 7，装完提示缺 DLL。

== 根因不是漏打包，是 Python 版本越界 ==

    Python 3.8    -> 最后一个支持 Windows 7 的版本
    Python 3.9+   -> 只支持 Windows 8 及以上

Python 3.9 起用到了 api-ms-win-core-path-l1-1-0.dll 里的
PathCchCanonicalizeEx，而这个 DLL **Windows 7 上根本不存在**，
也没法用 api-set 转发（那套机制只在 Win8+ 生效）。

所以之前用 3.11 打出来的包，在 Win7 上必然报缺 DLL ——
**那个 DLL 不是"忘了放进包"，是"放进去也没用"。**

顺带说明：包里那 47 个 api-ms-win-core-*.dll 转发库正是
Python 为兼容 Win7 才带上的 —— 上游本来就考虑过 Win7，
只是 3.11 这个版本号越界了。

== 改法 ==

1. **改用 Python 3.8 打包，而且选 32 位**
   32 位在 Win7 的 32 位和 64 位系统上都能跑，学校不用分两种发。
   （64 位的包在 32 位 Windows 上直接起不来。）

   依赖固定到 3.8 能用的版本：
       Pillow==9.5.0（最后一个支持 3.8 的）
       pyinstaller 6.x（保持 _internal 布局，和安装包/测试/卸载逻辑一致；
                       5.x 会把依赖摊在根目录，导致"_internal 在"那一项失败）

2. **打包脚本主动拦**
   windows\build_exe.ps1 打包前先校验 Python：
     * 找不到 3.8 -> 报错并给出安装命令
     * 版本不是 3.8 -> 直接 throw，而不是默默打一个别人装不上的包
     * 不是 32 位 -> 警告
   Python 从 YKXMZ_PY 环境变量或常见路径里找，不写死。

3. **新增 Win7 兼容校验** _build\check_win7_compat.py
   盯三件事：内嵌是不是 3.8 的运行库、**有没有**那个要命的 DLL、
   exe 是不是 32 位。打包时和回归套件（3a9 关）里都会跑，
   不过就拒绝出包。

== 顺手修的 ==

_build/test_installer_cycle.py 里写死了 ykxmz-setup-1.8.2.exe，
于是一直在**装一个不存在的旧包** -> 静默失败 -> 报出一堆
莫名其妙的失败（"配对码实际是 xxx"、"版本显示 v1.8.2"）。
改成自动找 dist 下版本号最新的包（按数字段排序，不能按字符串，
否则 1.9.0 会排到 2.1.4 前面）。

== 验证（用户要求：自己测一遍，没问题再上传）==

    全量回归 42 个阶段全 PASS（含新的 "Windows 7 兼容" 关）
    安装包完整周期 20/20
    Win7 校验：python38.dll / 无 api-ms-win-core-path / 32 位
    实际启动打包的 EXE：进程活着、TCP 监听、UDP 自动发现在听、
                        连接能建立（日志里有"设备接入"）
    产物扫描：三件全部干净（无旧密码、无服务器 IP）
    提交前泄漏闸门通过

安装包体积 14.4 MB -> 9.0 MB。
