mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 11:20:43 +00:00
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。
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 165 KiB |
+104
-6
@@ -11,24 +11,115 @@
|
||||
#
|
||||
# 注意:本版本**不再使用系统托盘**(pystray 已移除),
|
||||
# 关窗后进程继续运行,靠单实例机制再次双击找回窗口。
|
||||
#
|
||||
# ============================================================================
|
||||
# ⚠⚠ 必须用 Python 3.8 打包 —— 这是学校电脑能用的前提 ⚠⚠
|
||||
# ============================================================================
|
||||
#
|
||||
# 用户的学校电脑是 **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 打出来的 exe 在 Win7 上必然报缺 DLL。
|
||||
# 那个 DLL 不是"忘了放进包",是"放进去也没用"——只能在 Win8+ 跑。
|
||||
#
|
||||
# 这里选 **32 位** 3.8,因为 32 位在 Win7 的 32 位和 64 位系统上**都能跑**,
|
||||
# 一个包打天下,学校不用分两种发。
|
||||
# (64 位的包在 32 位 Windows 上直接跑不起来。)
|
||||
#
|
||||
# 脚本会**主动校验**:拿到的 Python 不是 3.8 就直接报错退出,
|
||||
# 而不是默默打出一个人家装不上的包。
|
||||
# ============================================================================
|
||||
$ErrorActionPreference = "Continue"
|
||||
|
||||
$windows = $PSScriptRoot # LanRemote/windows
|
||||
$root = Split-Path -Parent $windows # LanRemote
|
||||
|
||||
Write-Host "=== 1/3 生成图标 ===" -ForegroundColor Cyan
|
||||
& python (Join-Path $root "_build\make_ico.py")
|
||||
# ---------------------------------------------------------------- 选 Python
|
||||
Write-Host "=== 0/4 选择 Python(必须是 3.8,为了 Windows 7)===" -ForegroundColor Cyan
|
||||
|
||||
function Find-Py38 {
|
||||
# 1) 环境变量指定
|
||||
if ($env:YKXMZ_PY -and (Test-Path $env:YKXMZ_PY)) { return $env:YKXMZ_PY }
|
||||
# 2) 常见安装位置
|
||||
$cands = @(
|
||||
"C:\ykxmz-py38\python.exe",
|
||||
"C:\Python38\python.exe",
|
||||
"$env:LOCALAPPDATA\Programs\Python\Python38\python.exe",
|
||||
"$env:LOCALAPPDATA\Programs\Python\Python38-32\python.exe",
|
||||
"C:\Program Files (x86)\Python38-32\python.exe"
|
||||
)
|
||||
foreach ($c in $cands) { if (Test-Path $c) { return $c } }
|
||||
# 3) py launcher
|
||||
try {
|
||||
$p = & py -3.8 -c "import sys;print(sys.executable)" 2>$null
|
||||
if ($LASTEXITCODE -eq 0 -and $p -and (Test-Path $p.Trim())) {
|
||||
return $p.Trim()
|
||||
}
|
||||
} catch { }
|
||||
return $null
|
||||
}
|
||||
|
||||
$py = Find-Py38
|
||||
if (-not $py) {
|
||||
Write-Host @"
|
||||
|
||||
✗ 找不到 Python 3.8。
|
||||
|
||||
这个项目**必须**用 3.8 打包,因为学校电脑是 Windows 7,
|
||||
而 Python 3.9 及以上在 Win7 上会因为缺
|
||||
api-ms-win-core-path-l1-1-0.dll 直接启动失败。
|
||||
|
||||
装一个(32 位,Win7 32/64 位都能用):
|
||||
|
||||
https://www.python.org/ftp/python/3.8.10/python-3.8.10.exe
|
||||
|
||||
静默安装到 C:\ykxmz-py38 :
|
||||
python-3.8.10.exe /quiet InstallAllUsers=0 TargetDir=C:\ykxmz-py38 ``
|
||||
Include_pip=1 Include_tcltk=1 Include_test=0 Include_launcher=0
|
||||
|
||||
再装依赖:
|
||||
C:\ykxmz-py38\python.exe -m pip install Pillow==9.5.0 pyinstaller==5.13.2
|
||||
|
||||
或者设环境变量 YKXMZ_PY 指向已有的 3.8。
|
||||
|
||||
"@ -ForegroundColor Red
|
||||
throw "需要 Python 3.8"
|
||||
}
|
||||
|
||||
$ver = & $py -c "import sys;print('%d.%d'%sys.version_info[:2])"
|
||||
$bits = & $py -c "import struct;print(struct.calcsize('P')*8)"
|
||||
Write-Host " Python: $py"
|
||||
Write-Host " 版本: $ver ($bits 位)"
|
||||
|
||||
if ($ver -ne "3.8") {
|
||||
throw "打包必须用 Python 3.8(当前 $ver)。用 3.9+ 打出来的包在 Windows 7 上会因为缺 api-ms-win-core-path-l1-1-0.dll 启动失败。"
|
||||
}
|
||||
if ($bits -ne "32") {
|
||||
Write-Host " ⚠ 不是 32 位 —— 64 位的包在 32 位 Windows(不少学校机器是)上跑不起来。" -ForegroundColor Yellow
|
||||
Write-Host " 建议换成 32 位的 3.8。继续打包……" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
Write-Host "`n=== 1/4 生成图标 ===" -ForegroundColor Cyan
|
||||
& $py (Join-Path $root "_build\make_ico.py")
|
||||
if ($LASTEXITCODE -ne 0) { throw "生成图标失败" }
|
||||
$ico = Join-Path $windows "app.ico"
|
||||
|
||||
Write-Host "`n=== 2/3 PyInstaller 打包(--onedir 多文件)===" -ForegroundColor Cyan
|
||||
Write-Host "`n=== 2/4 PyInstaller 打包(--onedir 多文件)===" -ForegroundColor Cyan
|
||||
# ⚠ lanremote.ui 必须显式 --hidden-import(见下面那行)。
|
||||
# 它是"设置窗口"(tkinter)的实现,而静默那条路不需要它,
|
||||
# PyInstaller 静态分析就找不到 —— 于是双击 exe 想弹设置窗口时
|
||||
# 会 ModuleNotFoundError: No module named 'lanremote.ui',
|
||||
# 进程直接退出,用户看到的就是"双击没反应"。
|
||||
# (PS 的续行符 ` 后面不能再跟 # 注释,所以说明写在这里。)
|
||||
& python -m PyInstaller --noconfirm --clean --onedir --windowed `
|
||||
& $py -m PyInstaller --noconfirm --clean --onedir --windowed `
|
||||
--name "ykxmz" `
|
||||
--icon "$ico" `
|
||||
--paths "$windows" `
|
||||
@@ -50,9 +141,16 @@ Write-Host "`n=== 2/3 PyInstaller 打包(--onedir 多文件)===" -Foreground
|
||||
$target = Join-Path $root "dist_exe\ykxmz\ykxmz.exe"
|
||||
if (-not (Test-Path $target)) { throw "打包失败:未生成 $target" }
|
||||
|
||||
Write-Host "`n=== 3/3 产物 ===" -ForegroundColor Cyan
|
||||
# ---------------------------------------------------------------- Win7 校验
|
||||
Write-Host "`n=== 3/4 Windows 7 兼容性校验 ===" -ForegroundColor Cyan
|
||||
& $py (Join-Path $root "_build\check_win7_compat.py")
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Win7 兼容性校验没过 —— 这个包学校电脑装不上,不该发布。"
|
||||
}
|
||||
|
||||
Write-Host "`n=== 4/4 产物 ===" -ForegroundColor Cyan
|
||||
Write-Host "目录: $(Split-Path -Parent $target)"
|
||||
Write-Host ("exe : {0:N1} MB" -f ((Get-Item $target).Length / 1MB))
|
||||
$all = Get-ChildItem (Split-Path -Parent $target) -Recurse -File
|
||||
Write-Host ("总计: {0} 个文件, {1:N1} MB" -f $all.Count, (($all | Measure-Object Length -Sum).Sum / 1MB))
|
||||
Write-Host "`n完成" -ForegroundColor Green
|
||||
Write-Host "`n完成(Python $ver $bits 位,可在 Windows 7 及以上运行)" -ForegroundColor Green
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""局域网遥控 · Windows 被控端。"""
|
||||
|
||||
__version__ = "2.1.4"
|
||||
__version__ = "2.1.5"
|
||||
APP_NAME = "ykxmz"
|
||||
|
||||
@@ -27,12 +27,12 @@ VSVersionInfo(
|
||||
[
|
||||
StringStruct("CompanyName", "ykxmz"),
|
||||
StringStruct("FileDescription", "ykxmz"),
|
||||
StringStruct("FileVersion", "2.1.4.0"),
|
||||
StringStruct("FileVersion", "2.1.5.0"),
|
||||
StringStruct("InternalName", "ykxmz"),
|
||||
StringStruct("LegalCopyright", "ykxmz"),
|
||||
StringStruct("OriginalFilename", "ykxmz.exe"),
|
||||
StringStruct("ProductName", "ykxmz"),
|
||||
StringStruct("ProductVersion", "2.1.4.0"),
|
||||
StringStruct("ProductVersion", "2.1.5.0"),
|
||||
],
|
||||
)
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user