Repository navigation
【Bug】Windows 桌面端:安装目录被第三方进程占用时更新必然失败,且失败静默(MoveFileExW error 32) #10192
Replies: 3 comments 1 reply
|
感谢这份报告。我遇到了几乎一模一样的问题,然后叫ai以此报告为参考对象,最终定位到的元凶是搜狗输入法。 附件中是完整的事件报告。 |
|
补充一份本机实测数据(Windows 11 22621,安装目录为非默认路径 1. 第三方占用为 0,这台机器是纯自锁原帖把元凶定位到常驻第三方程序(搜狗
也就是说,不需要任何第三方常驻程序,这个缺陷也能稳定复现。 2.
|
| 时刻 | 事件 |
|---|---|
| 00:34:02 | 应用仍在写会话文件(进程还活着) |
| 00:34:03 | 安装器被拉起(Prefetch 记录) |
| 00:34:28 | 第 5 次 MoveFileExW error 32,25 秒内 10 次重试全部失败 |
安装器开工时旧进程尚未退完,目录必然被锁。这条不依赖第三方,靠自己就能触发。
3. 点窗口 × 不会退出,只是隐藏到托盘
这一点值得单独强调,因为它是「我已经退出了」这个误判的主要来源。主进程代码里窗口关闭被拦截后改为隐藏:
// resources/app.asar → lib/main.js
window.on("close", (event) => {
if (quitting || shellInstallerOwnsQuit || sessionEnding) return;
event.preventDefault(); // 拦掉关闭
// ...
hideMainWindow(window); // 改为隐藏到托盘
});真正的退出入口是托盘右键菜单里的「退出 DeepSeek Harness」。首次隐藏还会弹一次确认框,文案为「正在运行的任务不会中断,可在系统托盘中重新打开窗口」。
4. 判断「可以安装了」的正确标准
本机实测发现:进程数归零并不是充分条件。曾出现「退出应用后仍失败」,根因正是上面第 2 条的时序竞态。
建议改用安装器真正需要的条件来判断——目录能否以 DELETE 访问且按全共享打开(重命名要求持有者提供 FILE_SHARE_DELETE):
Add-Type -Namespace Win32 -Name DirHold -MemberDefinition '[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)] public static extern IntPtr CreateFileW(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);'
$dir = 'D:\DEEPSEEK'
# DELETE 访问 + READ|WRITE|DELETE 共享(7);返回 -1 表示有进程以不含 FILE_SHARE_DELETE 的方式占着
$h = [Win32.DirHold]::CreateFileW($dir, 0x00010000, 7, [IntPtr]::Zero, 3, 0x02000000, [IntPtr]::Zero)
if ($h -eq [IntPtr]::new(-1)) {
$code = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
"仍被占用: win32 $code $([ComponentModel.Win32Exception]::new($code).Message)"
} else { '可重命名' }本机结果很能说明问题:
| 目标 | 能否重命名 |
|---|---|
D:\DEEPSEEK(目录) |
被占用,win32 32 |
D:\DEEPSEEK\resources\app.asar |
被占用,win32 32 |
D:\DEEPSEEK\DeepSeek Harness.exe |
可重命名 |
注意 DeepSeek Harness.exe 本身是自由的,被锁的是目录和 app.asar。安装器要改的是整个目录,所以照样失败。这也解释了为什么只检查「exe 有没有被锁」会误判。
5. 最终生效的做法
在应用外部用独立 PowerShell 完成:先结束 DSH 全部进程 → 轮询确认目录可独占重命名 → 再运行缓存中已校验的安装包。
本机对照:
- 00:34:03 安装器启动(此时应用还活着)→ 00:34:28 失败
- 00:58:54 安装器启动(进程已清空)→ 00:59:17 目录被替换 → 00:59:27 新版启动,
0.2.2-rc.2安装成功
安装包一直在本机缓存里,sha512 与 update-info.json 记录一致、签名 Valid,无需重新下载。
6. 两点补充
- 自定义 NSIS 插件在版本间有变化:本机
window-frame.dll为 169,968 字节,sha2569FBB8F40091CB73722E60348A3DF397B132F150ED7D7EC6CCB20E3CE68968032;原帖附件记录的是 161,256 字节 /E8E361AD…E6D6CA。但「整目录改名 + 10 次重试 + 失败静默」的行为一致。 - 残留未清理依然存在:本机
@deepseek-aidsh-desktop-updater目录占 572.2 MB(installer.exe与pending\*.exe是同一份 299,659,528 字节安装包的两份拷贝)。
原帖第六节的建议里,第 2 条(修 quitAndInstall() 时序)可以比现在更靠前——它不需要任何外部条件,靠自己就能触发失败;第 1 条(去掉整目录改名,或改名失败时降级为覆盖安装)依然是最彻底的修法。
|
用你的方法成功解决 |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
@deepseek-ai/dsh-desktop0.2.0-rc.2(更新失败)→ 0.2.1-rc.2(手工装上)10.0.26220),简体中文,per-user 安装…Harness.old-{GUID}"这一步要求目录可独占重命名;只要有任何进程以不含FILE_SHARE_DELETE的方式打开着该目录(本机实测是搜狗输入法的SOGOUSmartAssistant.exe),MoveFileExW就报 32、重试 10 次后放弃 —— 更新失败、用户看不到任何提示、应用被关掉后不自动回来。二、环境
@deepseek-ai/dsh-desktop,0.2.0-rc.2 → 0.2.1-rc.2,Electron 44%LOCALAPPDATA%\Programs\DeepSeek Harness(per-user NSIS,注册表…\Uninstall\1bf39983-50d0-5fe0-9ef4-cece76f67c5e)electron-updater6.8.9(在resources\app.asar内),channelnightlyhttps://download.deepseek.com/dsh-desk/feeds/win-x64/nightly.ymldeepseek-harness-0.2.1-rc.2-win-x64.exe,289,385,456 B,sha512 与本机文件逐字节一致,签名Valid(CN="Hangzhou DeepSeek Artificial Intelligence Co., Ltd.")%LOCALAPPDATA%\@deepseek-aidsh-desktop-updater\(installer.exe、pending\…exe、installer-logs\)三、复现
A. 真实场景
SOGOUSmartAssistant.exe,随输入法常驻);%LOCALAPPDATA%\@deepseek-aidsh-desktop-updater\installer-logs\多出一份directory-failure-<时间戳>.log。B. 最小复现(与 DSH 无关,段 5 行;本机已实测)
share mode 扫描(2026-10-19 本机实测,每组都是新建目录 + 新句柄):
FILE_SHARE_READREAD|WRITEREAD|DELETEREAD|WRITE|DELETE结论:占用者没有给
FILE_SHARE_DELETE时,目录就无法重命名。这也解释了为什么 Explorer 偶尔持有的句柄(全共享)不致命,而搜狗那个句柄必然卡死。四、证据
4.1 三份失败日志(除 GUID/时间外逐字节一致,各 593 B)
三份分别对应 00:08:10、00:11:35(用户点"重启并安装")、00:49:39(应用已全部退出后手工跑安装包)。
4.2 关键对照:排除了"应用自己锁的目录"
Get-Process -Name 'DeepSeek Harness'为 0 个进程,且以FILE_SHARE_NONE打开DeepSeek Harness.exe成功(说明 exe 未被锁)→ 仍然同一个 error 32。handle64.exe -a "<安装目录>"扫描(01:07:45 与 01:09:01 两次采样一致):唯一持续持有该目录句柄的非 DSH 进程是deepseek-harness-0.2.1-rc.2-win-x64.exe /S --force-run退出码0,31.5 秒完成,版本变为 0.2.1-rc.2。4.3 相关代码位置
resources\app.asar→node_modules\electron-updater\out\BaseUpdater.js第 13–27 行:quitAndInstall()先install()(spawn 安装器)再app.quit()(setImmediate)—— 应用还没退出,安装器就已经开始改名了,这一条靠自己就能触发 4.1 的失败。%TEMP%\ns*.tmp\window-frame.dll(161,256 B,sha256E8E361AD93A5DC40EF7E39805D56A3CD2E691C8C8CC8A118271E43754EE6D6CA,无版本信息资源),.old-{GUID}改名与directory-failure-*.log均由它输出。五、影响面
--force-run只在成功路径生效),用户只能自己发现版本没变;installer.exe与pending\*.exe各 289 MB(同一安装包两份,共 578 MB)、三份失败日志、%TEMP%\ns*.tmp三个目录。六、建议修法(按优先级)
.old-{GUID}"(复制不要求独占),或在改名失败时降级为覆盖安装而不是中止更新。quitAndInstall()时序:先退出应用,再由独立进程(或app.on('quit')之后)启动安装器。pending\、installer.exe、%TEMP%\ns*.tmp。七、用户侧临时绕过
八、附件(同目录
evidence\)directory-failure-20261019-00810.log/-01135.log/-04939.loghandle64-scan-20261019-0104.txtwindow-frame.dllapply-update.logRESULT.txtverdict: updated)apply-update.log
directory-failure-20261019-00810.log
directory-failure-20261019-01135.log
directory-failure-20261019-04939.log
handle64-scan-20261019-0104.txt
RESULT.txt
All reactions