摘要:如果你的 ChatGPT Windows 客户端安装在 D 盘,更新后突然出现点击图标没反应、任务管理器里有进程、CPU 占用较高但始终没有窗口,可以尝试把应用移动到 C 盘。
本文不只提供解决方法,还会结合 Windows 事件日志、应用日志、残留文件和客户端代码,分析它为什么有效。
一、故障现象
这次问题出现在 Microsoft Store 版本的 ChatGPT Windows 客户端上。
故障表现包括:
- 点击 ChatGPT 图标没有任何窗口;
- 任务管理器中能看到多个 ChatGPT.exe 进程;
- 主进程没有窗口句柄;
- 某个 CPU 线程持续高占用;
- 等待很久也没有进入登录界面或主界面;
- 重启电脑、结束进程、卸载重装后仍可能复现;
- 在 Windows 设置中把 ChatGPT 移动到 C 盘后,应用立即恢复。
如果你的现象与这些特征基本一致,本文的解决方法很可能适用。
不过需要强调:并不是所有ChatGPT 无法启动都由这个问题引起。如果应用原本就在 C 盘,或者日志中没有运行时暂存痕迹,还需要继续排查显卡、权限、配置文件或应用数据损坏等其他原因。
二、先说解决方法
推荐方法:把 ChatGPT 移动到 C 盘
打开 Windows 设置:
text
设置
→ 应用
→ 已安装的应用
→ ChatGPT
→ 移动
→ 选择 C 盘
不同 Windows 版本的入口可能略有差异,移动按钮也可能位于三点菜单或高级选项中。
移动完成后,重新打开 ChatGPT,然后就可以看到ChatGPT的页面了。
在本次故障中,同一个应用版本移动到 C 盘后,不需要清除数据或重新登录,窗口就恢复了。
备用方案:没有移动按钮时
可以先修改 Microsoft Store 新应用的默认安装位置:
text
设置
→ 系统
→ 存储
→ 高级存储设置
→ 新内容的保存位置
→ 新应用将保存到 C 盘
然后重新安装 ChatGPT。
卸载应用可能会清理一部分本地缓存和应用状态,因此操作前最好备份重要的本地内容。
不要手动移动 WindowsApps 文件夹,也不要随意修改它的所有者和访问权限。这可能破坏 Microsoft Store 的更新和完整性校验。
三、最初的判断:是不是文件在 C、注册在 D?
刚开始排查时,一个很容易出现的判断是:
应用注册在 D 盘,但更新后的文件被装到了 C:\Program Files\WindowsApps,因此形成了跨盘分裂状态。
后来通过 Windows 事件日志确认,这个结论并不正确。
AppX 部署日志显示,出现问题的两个版本都明确部署到了 D 盘:
text
OpenAI.Codex_26.825.4187.0
Target volume: D:
OpenAI.Codex_26.825.6671.0
Target volume: D:
Windows Error Reporting 记录的实际可执行文件路径也是:
text
D:\WindowsApps\OpenAI.Codex_26.825.6671.0_x64__...\app\ChatGPT.exe
因此,更新后的文件并没有偷偷跑到 C 盘。
之所以某些查询结果会显示:
text
C:\Program Files\WindowsApps\OpenAI.Codex_...
是因为 Windows 会为非系统盘上的 AppX/MSIX 包建立逻辑入口或目录联接。表面路径可能位于 C 盘,物理文件实际上仍然位于 D 盘。
Microsoft 官方也说明,PackageVolume 不只可以位于 C 盘,也可以建立在其他驱动器上:Understanding how packaged desktop apps run on Windows。
所以,真正的问题并不是注册和文件分裂。
四、真正卡住的步骤:搬运 cua_node 运行时
ChatGPT 当前的 Windows 包中包含一个名为 cua_node 的运行时,里面包括:
- node.exe;
- node_repl.exe;
- npm 相关文件;
- Playwright;
- OCR、图像和浏览器自动化相关依赖;
- 大量 node_modules。
在本次版本中,这个目录大约包含:
text
文件数量:4680
总大小:318.88 MiB
应用启动时不会直接从受保护的 WindowsApps 目录使用这些文件,而是要把它们复制到用户目录:
text
%LOCALAPPDATA%\OpenAI\Codex\runtimes\cua_node\
复制过程中会先建立类似下面的临时目录:
text
.staging-e4d75eceaa042f20-HCFkgX
.staging-e4d75eceaa042f20-naQjRk
.staging-e4d75eceaa042f20-8iFoyJ
全部复制完成后,暂存目录才会被重命名为正式运行时目录。
问题就发生在这个过程里。
五、本地留下了大量未完成的暂存目录
排查时,在本地发现了:
text
失败的 .staging-* 目录:21 个
残留文件:23832 个
占用空间:约 3.78 GiB
这些目录都没有最终的 manifest.json,说明运行时复制从未正常完成。
几个典型启动过程如下:
| 启动时间 | 暂存目录最后写入 | 进程结束 | 已复制数据 |
|---|---|---|---|
| 10:22:20 | 10:36:13.775 | 10:36:14.094 | 257.05 MiB |
| 10:46:30 | 10:58:45.949 | 10:58:46.225 | 262.65 MiB |
| 11:02:09 | 11:13:37.231 | 11:13:37.895 | 239.06 MiB |
暂存文件停止写入的时间与应用进程结束时间几乎完全一致。
这说明应用并非完全没有执行,而是一直在后台复制运行时。由于复制过程没有界面和进度提示,看起来就像点击图标毫无反应。
其中一次复制持续了约 15 分 40 秒,平均速度只有大约:
text
0.28 MiB/s
即使复制了十几分钟,仍然没有完成。
六、为什么 D 盘上的复制会这么慢?
进一步检查发现:
- C、D 两个分区位于同一块 NVMe SSD;
- 两个分区健康状态正常;
- 因此不是 D 盘硬盘性能太差;
- D 盘上的 AppX 物理包目录带有 Encrypted 属性;
- 复制目标位于 C 盘普通的 %LOCALAPPDATA% 目录。
换句话说,应用是在把 D 盘加密的 AppX 包资源复制到 C 盘的普通用户目录。
更关键的是,客户端代码使用了同步文件操作。
其逻辑可以简化为:
js
// 同步读取并计算运行时文件哈希
hash = sha256(readFileSync(source))
// 同步递归遍历整个 cua_node
for (const file of files) {
copyFileSync(source, destination)
}
代码还专门处理了 Windows 错误码 6000:
js
try {
copyFileSync(source, destination)
} catch (error) {
if (error.errno === 6000) {
writeFileSync(destination, readFileSync(source))
}
}
Windows 错误码 6000 对应 ERROR_ENCRYPTION_FAILED,即加密文件复制失败。可以在 Microsoft System Error Codes 6000--8199 中查到。
当普通的 copyFileSync 无法直接处理加密资源时,应用会退化为:
- 把整个文件同步读入内存;
- 再同步写入目标目录;
- 对数千个文件重复这个过程。
而这一切发生在 Electron 主进程中。
七、为什么会出现单线程高占用、始终没有窗口?
Electron 的窗口创建、事件处理和一部分启动逻辑都依赖主线程。
但 ChatGPT 在窗口创建前执行了大量同步操作:
- 同步读取约 119 MiB 的关键文件并计算 SHA-256;
- 同步遍历 4680 个文件;
- 同步复制约 319 MiB 的运行时;
- 遇到加密复制错误后,继续同步读写回退。
只要这些操作没有结束,主线程就无法及时处理窗口创建和界面事件。
最终表现就是:
text
进程存在
CPU 单线程高占用
没有渲染进程或窗口
应用日志不再继续输出
失败启动的应用日志通常只留下三行:
text
in_app_updates_policy_wait_started
Launching app
Appshot hotkey inactive
这并不意味着更新检查或 Appshot 本身是根因。它们只是主线程进入同步运行时搬运之前,最后成功写入日志的步骤。
Windows 最终也记录了正式的应用挂起事件:
text
EventName: MoAppHang
HangType: Quiesce
AppName: ChatGPT.exe
八、为什么移动到 C 盘后立即恢复?
Windows 在设置中执行移动后,同一个应用版本从 D 盘迁移到了 C 盘。
移动前:
text
应用包:D:\WindowsApps\...
运行时目标:C:\Users\...\AppData\Local\OpenAI\Codex\...
应用包属性:Encrypted
移动后:
text
应用包:C:\Program Files\WindowsApps\...
运行时目标:C:\Users\...\AppData\Local\OpenAI\Codex\...
应用包不再来自 D 盘加密 AppX 卷
移动后的首次成功启动时间线为:
text
12:07:44 启动同一个 26.825.6671.0 版本
12:07:47 开始创建完整运行时目录
12:07:56 318.88 MiB、4680 个文件复制完成
12:08:06 主窗口完成加载并显示
原来十几分钟都无法完成的复制,在 C 盘大约 9 秒就完成了。
因此,移动到 C 盘有效的决定性原因不是修复了所谓的注册分裂,而是:
它让 ChatGPT 不再从 D 盘加密的 AppX 包目录同步搬运运行时,极大降低了启动阶段的复制成本。
九、这是 Windows 的问题,还是 ChatGPT 的问题?
两边的设计共同触发了问题,但更直接的缺陷在应用侧。
Windows 允许 Microsoft Store/MSIX 应用安装在非系统卷,这本身是受支持的能力。问题在于 ChatGPT:
- 在窗口创建前搬运数百 MiB 运行时;
- 使用同步递归复制;
- 在主线程上同步计算大文件哈希;
- 对加密复制失败逐文件执行同步回退;
- 没有启动进度提示;
- 没有合理超时;
- 没有把耗时操作放到工作线程;
- 进程被结束后留下大量未完成暂存目录。
更合理的实现方式应该包括:
- 将运行时物化放入后台线程或独立进程;
- 先创建启动窗口并显示进度;
- 使用异步流式复制;
- 对 EFS 加密包使用专门的解密目标复制策略;
- 为失败复制提供日志、超时和重试上限;
- 下次启动时清理或复用有效的暂存数据。
因此,这更像是 ChatGPT Windows 客户端在非系统 AppX 卷场景下的启动健壮性问题。
十、如何判断自己是不是同一个问题?
可以先检查 ChatGPT 包信息:
powershell
$OutputEncoding = [Console]::OutputEncoding = [Text.UTF8Encoding]::new($false)
Get-AppxPackage -Name OpenAI.Codex |
Select-Object Name, Version, InstallLocation, Status
需要注意:InstallLocation 可能显示 C 盘逻辑入口,不能仅凭这一项判断物理文件在哪个盘。
然后检查运行时目录:
powershell
$runtimeRoot = Join-Path $env:LOCALAPPDATA "OpenAI/Codex/runtimes/cua_node"
Get-ChildItem -LiteralPath $runtimeRoot -Force -Directory |
Select-Object Name, CreationTime, LastWriteTime
如果存在大量这样的目录:
text
.staging-xxxxxxxxxxxxxxxx-xxxxxx
而没有对应的完整 16 位哈希目录,就很可能卡在运行时搬运阶段。
应用日志通常位于:
text
%LOCALAPPDATA%\Packages\OpenAI.Codex_2p2nqsd0c76g0\
LocalCache\Local\Codex\Logs
还可以检查最近的应用挂起事件:
powershell
Get-WinEvent -FilterHashtable @{
LogName = "Application"
Id = 1002
StartTime = (Get-Date).AddDays(-7)
} | Where-Object {
($_.Properties.Value -join "|") -match "ChatGPT|OpenAI\.Codex"
} | Select-Object TimeCreated, Id, RecordId
十一、总结
如果你足够有耐心的话,可以不修改这个 bug,理论上,你只要等足够长的时间,它就会打开。因为我之前就有一次,当时看着其他文档时,它突然蹦了出来,当时以为是什么时候不小心点到了,可能实际上是它从 D 盘复制到 C 盘成功了。
不过需要提醒:等待能否成功,取决于最新 staging 目录是否仍在持续增长。如果文件数、大小和修改时间长时间不再变化,或者不断产生新的 staging 目录,继续等待通常没有意义,建议直接按上文方法移动到 C 盘。
十二、相关公开问题
OpenAI 的公开问题列表中已经有高度相似的报告:
这些公开问题证明并非个别机器才会出现该现象,但本文分析的同步运行时搬运和 EFS 回退机制,主要来自本机日志、残留文件与客户端包内代码的交叉验证,尚不能代表 OpenAI 官方已经确认相同根因。
十三、为什么以前在 D 盘没事,更新后才出现?
1. 正常启动不一定每次都复制 319 MiB
cua_node 的正式缓存目录由运行时内容哈希标识。启动时如果已经存在与当前包匹配、带完整清单的缓存目录,客户端可以直接复用;只有缓存缺失、哈希变化或缓存不完整时,才需要创建新的 .staging-* 并重新搬运整套运行时。
因此,同一个应用在 D 盘连续正常使用数月并不矛盾。旧版本很可能一直命中已经完成的缓存,没有在每次启动时重新复制数千个受保护文件。
2. 这次更新改变了启动前提
本机事件和文件时间线如下:
| 时间 | 发生的事情 |
|---|---|
| 更新前 | ChatGPT 一直安装在 D 盘,并能正常启动 |
| 8 月 28 日 23:08 左右 | 26.825.4187.0 开始部署到 D 盘 |
| 后续故障启动 | 新运行时哈希没有对应的完整缓存,客户端反复创建 .staging-*;共留下 21 个未完成目录 |
| 9 月 1 日 11:48 | 26.825.6671.0 仍注册到 D 盘 |
| 11:50、12:00 | 26.825.6671.0 在 D 盘启动,仍然只有进程、没有窗口 |
| 12:04 | Windows 将同一个 26.825.6671.0 成功移动到目标卷 C: |
| 12:07--12:08 | 同一版本重新启动,运行时约 9 秒准备完成,窗口随即出现 |
最符合这些证据的过程是:
text
旧版本日常启动
→ 已有完整的旧运行时缓存
→ 哈希匹配,直接复用
→ 正常创建窗口
更新后首次启动
→ 当前包需要另一份运行时缓存
→ 没有匹配的完整目录
→ 从 D:\WindowsApps 创建新的 staging
→ 受保护文件跨卷复制异常缓慢或无法完成
→ 主线程被占用,窗口无法创建
同一版本移动到 C 盘后
→ 从 C 盘重新物化运行时
→ staging 正常完成并转为正式目录
→ 窗口恢复
3. 为什么不能简单认为这次更新新引入了 bug?
OpenAI 公开仓库中的 #34764 在更早的 26.715 版本就记录过高度一致的现象:非系统 AppX 卷、WindowsApps 文件受保护、复制返回错误 6000,以及移动到 C 盘后恢复。这说明底层薄弱点至少在更早版本已经存在。
本机现在能够确认的是:
- 故障确实紧跟
26.825.4187.0更新出现; - 该版本的启动确实反复创建了新的运行时暂存目录;
- D 盘包资源确实带有
Encrypted属性; - 后续版本在 D 盘仍失败,而同一版本移动到 C 盘后成功。
但旧包已经被 Store 替换,无法再对比旧版本的 cua_node 内容、哈希、复制实现和文件保护属性,也就无法把变化精确定位到某一个 OpenAI 代码提交或某一次 Windows Store 部署行为。
所以更严谨的结论是:
8 月 28 日的更新改变了运行时缓存条件,迫使客户端重新物化
cua_node,从而在这台机器上首次暴露了一个此前已经潜伏的非系统 AppX 卷 + 受保护文件 + 主进程同步复制缺陷;它是本次故障的触发点,不等于已经证明它是缺陷的最初来源。
这也解释了为什么不是每次更新都会复发:如果某次更新继续使用同一个运行时哈希,完整缓存仍可复用;只有运行时内容变化、缓存失效或需要重建时,才更容易再次触发。
因为我的电脑这个问题已经修复了,之前的旧包也已经覆盖了,没有办法对比了,如果有大佬知道是为什么,可以在评论区说一下。
额外了解------为什么复制会这么慢?
最核心的一点是:这并不是一次普通的 D 盘到 C 盘文件复制。对于受影响的文件,客户端会先尝试保留源文件加密属性的正常复制;这条路径失败后,再退化为同步解密、读取和写入。
一个文件大致会经历下面的处理链:
text
读取源文件并计算哈希
↓
copyFileSync 尝试正常复制
↓
Windows 尝试把源文件的 EFS 加密信息应用到目标文件
↓
目标文件加密失败,返回错误 6000
↓
readFileSync 通过 EFS 解密并读取源文件
↓
writeFileSync 把内容写入 C 盘普通目录
↓
处理下一个文件
1. 正常复制路径会先失败一次
D 盘 WindowsApps 中的源文件带有 Encrypted 属性。根据 Microsoft 的说明,Windows 复制 EFS 加密文件时,会先尝试使用源文件的加密密钥加密目标文件;如果不成功,再尝试使用默认密钥。两种方式都失败后,复制操作才返回 ERROR_ENCRYPTION_FAILED,也就是错误码 6000。
参考:Microsoft:Handling Encrypted Files and Directories
因此,每个受影响的文件都可能先经历:
- 打开源文件;
- 查询加密属性和密钥;
- 调用 EFS 服务;
- 创建或准备目标文件;
- 尝试应用加密;
- 最后失败并抛出错误。
这次失败不一定已经复制了整个文件,但它会增加额外的文件系统、权限检查和加密服务开销。
2. 失败后还要重新读取并写入
从本地客户端包内代码观察到的简化逻辑如下:
js
try {
copyFileSync(source, destination)
} catch (error) {
if (error.errno === 6000) {
writeFileSync(destination, readFileSync(source))
}
}
发生错误 6000 后,客户端不再要求 Windows 保留原文件的加密状态,而是:
- 再次打开 D 盘上的加密源文件;
- 通过 EFS 解密路径读取文件内容;
- 将完整内容放入内存;
- 在 C 盘目标目录中创建普通文件;
- 把内存中的内容同步写入目标文件。
如果复制前还要计算 SHA-256,那么同一份数据还需要先被完整读取一次。一次处理可能包含:
text
第一次读取:计算哈希
第二次操作:尝试正常加密复制并失败
第三次读取:解密并读入内存
第四次操作:写入目标文件
因此,实际工作量明显高于资源管理器中的普通复制。
3. 大量小文件会放大固定开销
cua_node 运行时并不是一个连续的大文件,而是由大量 JavaScript、JSON、原生模块和依赖文件组成。
复制一个 1 GiB 的大文件,耗时通常主要取决于磁盘连续读写速度;复制数千个小文件时,大量时间则消耗在:
- 打开和关闭文件;
- 创建目录和文件;
- 查询权限、属性和加密信息;
- 分配与释放内存;
- 更新 NTFS 元数据;
- 对新生成的脚本、可执行文件和原生模块进行安全扫描。
最后一项是否发生、耗时多少取决于本机安全软件配置,并非本次日志已经确认的主因;但它可能进一步放大小文件写入的成本。
虽然 C、D 两个分区位于同一块 NVMe SSD,但它们仍是两个独立文件系统卷。跨卷操作需要真正读取源数据并写入目标卷,两个方向还会共享同一块 SSD 的控制器与闪存通道,不能因为位于同一块物理硬盘上就视为零成本操作。
不过,约 319 MiB 的正常复制本身不应该长时间卡住。真正异常的是加密处理、逐文件同步操作和失败后的重复劳动叠加。
4. 同步接口让复制串行化,也让窗口无法创建
copyFileSync、readFileSync 和 writeFileSync 都是同步接口:
同步接口并不会单独降低某一次内核复制的速度,但它有两个重要影响:
- 当前文件没有处理完之前,程序不能开始处理下一个文件;
- 如果这些操作运行在 Electron 主进程中,窗口创建和界面事件也必须等待。
也就是说,应用不是在后台慢慢准备、同时先显示一个启动界面,而是把窗口创建也堵在了运行时准备流程后面。于是任务管理器里能看到进程和单线程高占用,桌面上却始终没有窗口。
5. 3.78 GiB 是失败残留的累计值
本机排查时发现:
text
未完成的 .staging-* 目录:21 个
残留文件:23,832 个
占用空间:约 3.78 GiB
这不代表一次启动需要复制 3.78 GiB。它是多次未完成操作留下的累计结果。
每次启动都可能经历:
text
创建新的 staging 目录
↓
复制一部分运行时文件
↓
复制失败、进程退出或被用户结束
↓
下次启动创建另一个 staging 目录
↓
再次执行前面的哈希和复制
因此,用户感受到的"复制非常慢",有时实际是"大量重复劳动,并且每次都没有到达完成状态",而不是同一次复制一直在稳定前进。
多个 staging 目录只能证明发生过多次未完成的准备过程,不能单凭它判断这些过程全部由应用自动重试产生;反复点击图标、手动结束进程或重启电脑,同样可能留下新的暂存目录。
6. 为什么移动到 C 盘后很快恢复?
移动到 C 盘会触发一次重新部署。根据本机移动后的检查,同一版本不再触发原先的 EFS 跨卷复制失败,运行时准备可以走正常路径:
text
计算哈希
↓
正常复制
↓
staging 目录重命名为正式运行时目录
↓
创建应用窗口
本机实测,移动后同一版本的运行时准备约 9 秒完成。因此,改善的关键并不是"C 盘物理速度一定比 D 盘快",而是新的部署状态绕开了原先的加密复制失败和同步回退路径。
7. 是否只要等待足够久就能打开?
只有当所有文件最终都能成功处理,并且最新 staging 目录的文件数、大小和修改时间持续增长时,单次不受打断的启动才有可能在等待后完成。
如果出现以下情况,继续等待通常没有意义:
- 最新 staging 目录长时间不再变化;
- 不断产生新的 staging 目录;
- 始终无法生成最终 manifest.json;
- 同一个加密、权限或校验错误反复出现。
所以这次问题更准确的描述不是"D 盘太慢",而是:
D 盘 AppX 包的加密属性使正常复制路径失败,客户端随后采用同步、逐文件的解密读写方式兜底;大量小文件、哈希计算和多次未完成操作共同放大了启动时间,并阻塞了 Electron 窗口创建。