Codex 桌面版(Windows)启动后无 UI 窗口问题复盘
记录时间:2026-10-18@16:50:29
本文为个人排障记录,不代表 OpenAI 官方文档。文中的用户名、SID、窗口句柄等本机标识已做脱敏处理。
目录
- [Codex 桌面版(Windows)启动后无 UI 窗口问题复盘](#Codex 桌面版(Windows)启动后无 UI 窗口问题复盘)
-
- [1. 结论速览](#1. 结论速览)
- [2. 问题现象](#2. 问题现象)
- [3. 环境与对象](#3. 环境与对象)
- [4. 排查过程](#4. 排查过程)
-
- [4.1 进程与窗口分离](#4.1 进程与窗口分离)
- [4.2 启动链路与数据目录](#4.2 启动链路与数据目录)
- [4.3 运行时解压是阻塞点](#4.3 运行时解压是阻塞点)
- [4.4 EFS 加密放大耗时](#4.4 EFS 加密放大耗时)
- [4.5 事件日志与崩溃报告](#4.5 事件日志与崩溃报告)
- [4.6 排除项](#4.6 排除项)
- [5. 根因分析](#5. 根因分析)
- [6. 解决方案](#6. 解决方案)
-
- [6.1 临时方案(故障现场)](#6.1 临时方案(故障现场))
- [6.2 最终方案(标准流程)](#6.2 最终方案(标准流程))
- [6.3 不推荐操作](#6.3 不推荐操作)
- [7. 关键命令](#7. 关键命令)
-
- [7.1 识别进程与安装信息](#7.1 识别进程与安装信息)
- [7.2 启动应用](#7.2 启动应用)
- [7.3 监测解压进度](#7.3 监测解压进度)
- [7.4 检查 EFS 属性](#7.4 检查 EFS 属性)
- [7.5 检查挂起事件](#7.5 检查挂起事件)
- [7.6 清理旧 staging(精确路径)](#7.6 清理旧 staging(精确路径))
- [8. 最佳实践](#8. 最佳实践)
- [9. 知识框架与排障图谱](#9. 知识框架与排障图谱)
-
- [9.1 知识框架](#9.1 知识框架)
- [9.2 排障决策树](#9.2 排障决策树)
- [10. 附录](#10. 附录)
-
- [A. 窗口枚举脚本](#A. 窗口枚举脚本)
- [B. 时间线](#B. 时间线)
- [C. 术语](#C. 术语)
- [D. 参考](#D. 参考)
1. 结论速览
- 根因 :Codex 桌面版在更新后的首次启动中,于主线程同步解压自带
cua_node运行时(2369 个文件 / 240.6 MB)到%LOCALAPPDATA%\OpenAI\Codex\runtimes\cua_node。解压期间主窗口已创建但处于隐藏状态,因此表现为"任务管理器有进程、看不到 UI"。 - 放大因素 :MSIX 包文件带 EFS 加密属性,读取解密使解压耗时约 2 分 46 秒;每次中途结束进程都会从头解压,并留下
.staging-*残留目录。 - 最终方案:启动后等待解压完成(建议预留 3--10 分钟),不要结束进程;完成后窗口自动显示。
- 处置结果:2026-10-18 16:41:04 解压完成,窗口显示;清理 4 个旧版本 staging 目录,释放 745.4 MB。
- 排查关键 :
MainWindowHandle=0不代表窗口不存在;需用EnumWindows检查窗口句柄、可见性和矩形。
2. 问题现象
| 观察项 | 结果 |
|---|---|
| 进程 | ChatGPT.exe 多进程持续运行,启动初期 5 个,解压完成后增至 10 个 |
| 窗口 | MainWindowHandle=0;Win32 枚举发现窗口存在但 visible=False |
| 事件日志 | MoAppHang,旧版本 OpenAI.Codex_26.908.4834.0 |
| 用户操作 | 15:53--16:20 期间多次启动并结束进程,问题稳定复现 |
| 应用状态 | Get-AppxPackage 状态 Ok,无安装损坏迹象 |
影响:应用不可用,容易被误判为"启动崩溃""窗口跑到屏幕外"或"安装损坏"。
3. 环境与对象
| 对象 | 路径 / 值 |
|---|---|
| 操作系统 | Windows 11 10.0.26200(64-bit) |
| MSIX 包 | OpenAI.Codex_26.1013.4894.0_x64__2p2nqsd0c76g0 |
| AppID | OpenAI.Codex_2p2nqsd0c76g0!App |
| 安装目录 | C:\Program Files\WindowsApps\OpenAI.Codex_26.1013.4894.0_x64__2p2nqsd0c76g0 |
| 主程序 | ...\app\ChatGPT.exe(Chromium 155.0.8059.80) |
| 用户级运行时 | %LOCALAPPDATA%\OpenAI\Codex |
| 运行时解压目录 | %LOCALAPPDATA%\OpenAI\Codex\runtimes\cua_node |
| Chromium 配置 | %LOCALAPPDATA%\Packages\OpenAI.Codex_2p2nqsd0c76g0\LocalCache\Roaming\Codex\web\Codex |
| 启动日志 | ...\LocalCache\Local\Codex\Logs\YYYY\MM\DD\codex-desktop-*.log |
| 包数据重定向 | <数据盘>:\WpSystem\<SID>\AppData\Local\Packages\OpenAI.Codex_2p2nqsd0c76g0 |
说明:
- 包数据可能被重定向到
<数据盘>:\WpSystem\<SID>,与应用曾被移动到其他盘有关;这是正常机制,不是本次根因。 %USERPROFILE%\.vscode\extensions\openai.chatgpt-*\bin\windows-x86_64\codex.exe是 VS Code 扩展的后台服务,与桌面版 UI 无关。codex-windows-sandbox-service.exe是沙箱服务,也不是桌面版主进程。
4. 排查过程
4.1 进程与窗口分离
先用常规方式确认进程:
bash
powershell -NoProfile -Command 'Get-Process | Where-Object { $_.ProcessName -match "codex|chatgpt|openai" } | Select-Object Id,ProcessName,MainWindowHandle,MainWindowTitle,Path | Format-List'
结果:ChatGPT.exe 存在,但所有进程的 MainWindowHandle 均为 0。
随后用 Win32 EnumWindows 枚举这些 PID 的顶层窗口,发现:
| 阶段 | 窗口类 | 可见性 | 标题 | 矩形 |
|---|---|---|---|---|
| 解压期间 | Chrome_WidgetWin_0 |
False |
空 | (104,104)-(957,594),853×490 |
| 解压完成后 | Chrome_WidgetWin_1 |
True |
ChatGPT |
(307,129)-(1398,890),1091×761 |
结论:窗口并非不存在,而是在启动阶段被创建后保持隐藏;MainWindowHandle 对 Chromium 多进程应用的判断并不可靠。
4.2 启动链路与数据目录
MSIX 清单显示应用入口为 app/ChatGPT.exe,EntryPoint=Windows.FullTrustApplication。启动日志仅记录运行时组件解析:
text
2026-10-18T08:38:20.284Z info windows_core_runtime_component_resolved
appPackageCoreActive=false architecture=x64 component=codex consumer=resolver
copiedRuntimeLocation=true coreGateEnabledAtStartup=null
executablePath=C:\Users\<用户名>\AppData\Local\OpenAI\Codex\bin\<hash>\codex.exe
source=copied
该日志说明 codex.exe 已复制到用户目录,但日志在此之后不再更新,说明主要耗时不在核心二进制复制,而在后续的 cua_node 解压。
4.3 运行时解压是阻塞点
cua_node 目录下存在多个 .staging-* 目录,每次启动创建一个:
| 目录 | 最后写入 | 文件数 | 大小 | 结果 |
|---|---|---|---|---|
.staging-a708e72b10c27b59-28GhtX |
15:53:11 | 3167 | 202.9 MB | 旧版本,被终止 |
.staging-a708e72b10c27b59-Akbh1U |
15:55:38 | 914 | 185.1 MB | 旧版本,被终止 |
.staging-a708e72b10c27b59-uzLtF9 |
15:56:29 | 422 | 128.6 MB | 旧版本,被终止 |
.staging-a708e72b10c27b59-Y6lZLs |
15:57:42 | 3627 | 206.2 MB | 旧版本,被终止 |
.staging-78fb63cda1a9e2b2-iEDaB1 |
16:15:57 | 1978 | 198.8 MB | 当前版本,被终止 |
.staging-78fb63cda1a9e2b2-NkrVCm |
16:18:23 | 2113 | 200.6 MB | 当前版本,被终止 |
.staging-78fb63cda1a9e2b2-BaCiZR |
16:20:40 | 1773 | 195.1 MB | 当前版本,被终止 |
.staging-78fb63cda1a9e2b2-w7oApx |
16:38:20 | 2231 → 2369 | 194.2 → 240.6 MB | 16:41:04 完成并重命名 |
关键数据:
- 当前版本完整运行时为 2369 个文件 / 240.6 MB ,最终目录名
78fb63cda1a9e2b2。 - 本次启动到解压完成耗时约 2 分 46 秒 ;解压期间主进程持续占用约 0.9 核,30 秒内文件数增加 616,说明不是死锁。
- 新版本的三次早期尝试分别达到 1978 / 2113 / 1773 个文件时被终止,距离完成只差最后一段。
- 成功解压后,应用会清理同 hash 的 staging 目录;旧版本 hash 的残留不会自动清理。
4.4 EFS 加密放大耗时
安装目录中的文件带 Encrypted 属性:
text
ChatGPT.exe Archive, Encrypted
chrome.dll Archive, Encrypted
chrome_100_percent.pak Archive, Encrypted
读取这些文件需要先解密,再写入用户目录。cua_node 包含大量小文件,进一步放大了 I/O 开销。因此解压 240 MB 仍可能需要数分钟,且不同机器差异较大。
4.5 事件日志与崩溃报告
旧版本在 16:00:55 留下挂起报告:
text
Event Name: MoAppHang
P1: OpenAI.Codex_26.908.4834.0_x64__2p2nqsd0c76g0
P2: praid:App
P3: 152.0.7977.83
P5: b134
MoAppHang 表示 Windows 检测到应用在主线程上长时间无响应,与"窗口已创建但未显示"的表现一致。它不是崩溃(MoAppCrash),没有生成应用崩溃转储。
另有一条 RADAR_PRE_LEAK_64 记录指向 chrome.exe 的内存泄漏遥测,与本问题无关。
4.6 排除项
| 假设 | 验证 | 结论 |
|---|---|---|
| 窗口跑到屏幕外 | EnumWindows 显示窗口最初 visible=False,矩形在屏幕内 |
排除;后续曾出现在 (-21333,-21333),属于最小化状态 |
| 网络阻塞 | 解压期间 Get-NetTCPConnection 无该进程连接 |
排除 |
| 安装损坏 | Get-AppxPackage 状态 Ok,主程序可读 |
排除 |
| 与 VS Code 扩展冲突 | 扩展的 codex.exe 是独立后台服务 |
排除 |
| 应用未启动 | 5--10 个 ChatGPT.exe 进程持续运行 |
排除 |
5. 根因分析
主因 :Codex 桌面版在首次启动或版本更新后的第一次启动时,会在主线程同步完成 cua_node 运行时的解压/搬运。解压完成前,窗口保持隐藏,因此用户看到的是"有进程、无窗口"。
促成因素:
- 每次版本更新会更换 runtime hash(本次从
a708e72b10c27b59变为78fb63cda1a9e2b2),旧的解压结果不能复用。 - MSIX 包文件带 EFS 加密,读取解密增加了大量小文件的复制耗时。
- 用户在解压接近完成时结束进程,下一次启动重新开始,导致问题看起来"永远无法恢复",并累积
.staging-*残留目录。 - Chromium 主窗口在内容就绪前创建但不显示,
MainWindowHandle=0进一步误导了判断。
6. 解决方案
6.1 临时方案(故障现场)
| 方案 | 操作 | 适用性 |
|---|---|---|
| 等待解压完成 | 启动后不结束进程,观察 cua_node 下 staging 文件数是否增长 |
首选,本次生效 |
| 恢复最小化窗口 | 用 ShowWindow(hwnd, SW_RESTORE) + SetForegroundWindow |
窗口已显示但被最小化/移出屏幕时 |
| 修复/重置应用 | 设置 → 应用 → ChatGPT → 高级选项 → 修复/重置 | 等待 15--20 分钟仍无进展时的后备手段;会清除本地数据 |
本次实际采用的临时处置:启动应用 → 等待并监测 staging 目录 → 解压完成后窗口自动出现 → 对最小化窗口执行恢复。
6.2 最终方案(标准流程)
-
启动:通过 AppID 启动,避免直接执行 WindowsApps 内的 exe。
bashpowershell -NoProfile -Command 'Start-Process "shell:AppsFolder\OpenAI.Codex_2p2nqsd0c76g0!App"' -
等待并监测:更新后的首次启动预留 3--10 分钟;若 staging 文件数持续增长或主进程 CPU 持续占用,说明仍在推进,不要结束进程。
-
验证完成 :
runtimes\cua_node下出现与当前版本对应的最终目录(本次为78fb63cda1a9e2b2),且同 hash 的.staging-*消失。 -
验证窗口 :用
EnumWindows确认存在Chrome_WidgetWin_1、标题为ChatGPT、visible=True;必要时恢复窗口。 -
清理残留 :仅删除旧版本 hash 的
.staging-*目录,保留当前最终目录;使用精确路径,避免通配符误删。
6.3 不推荐操作
- 反复结束
ChatGPT.exe:会中断解压并从零开始,是本次问题持续的直接原因。 - 删除当前版本的最终运行时目录 (如
78fb63cda1a9e2b2):会导致下次启动重新解压。 - 结束 VS Code 扩展的
codex.exe:与桌面版 UI 无关,可能影响扩展功能。 - 未确认前直接重置/重装:修复/重置会清除本地数据,应放在"等待无效"之后。
7. 关键命令
以下命令在 Git Bash 中通过
powershell -NoProfile -Command执行。
7.1 识别进程与安装信息
bash
powershell -NoProfile -Command 'Get-Process | Where-Object { $_.ProcessName -match "chatgpt|codex" } | Select-Object Id,ProcessName,Responding,MainWindowHandle,MainWindowTitle,Path | Format-List'
bash
powershell -NoProfile -Command 'Get-CimInstance Win32_Process | Where-Object { $_.Name -eq "ChatGPT.exe" } | Select-Object ProcessId,ParentProcessId,ExecutablePath,CommandLine | Format-List'
bash
powershell -NoProfile -Command 'Get-AppxPackage OpenAI.Codex | Select-Object Name,Version,PackageFullName,InstallLocation,Status | Format-List'
7.2 启动应用
bash
powershell -NoProfile -Command 'Start-Process "shell:AppsFolder\OpenAI.Codex_2p2nqsd0c76g0!App"'
若 AppID 不同,先查询:
bash
powershell -NoProfile -Command 'Get-StartApps | Where-Object { $_.Name -match "ChatGPT|Codex" } | Format-List'
7.3 监测解压进度
bash
powershell -NoProfile -Command '
$rt="$env:LOCALAPPDATA\OpenAI\Codex\runtimes\cua_node"
$d=Get-ChildItem $rt -Directory -Force | Sort-Object LastWriteTime -Descending | Select-Object -First 1
$f=Get-ChildItem $d.FullName -Recurse -File -Force -ErrorAction SilentlyContinue
"{0} files={1} sizeMB={2}" -f $d.Name,$f.Count,[math]::Round(($f|Measure-Object Length -Sum).Sum/1MB,1)
'
7.4 检查 EFS 属性
bash
powershell -NoProfile -Command '(Get-Item "C:\Program Files\WindowsApps\OpenAI.Codex_*\app\ChatGPT.exe").Attributes'
7.5 检查挂起事件
bash
powershell -NoProfile -Command 'Get-WinEvent -FilterHashtable @{LogName="Application"; ProviderName="Windows Error Reporting"; StartTime=(Get-Date).AddDays(-1)} | Where-Object { $_.Message -match "OpenAI.Codex" } | Select-Object TimeCreated,Id,Message | Format-List'
7.6 清理旧 staging(精确路径)
执行前请确认路径和 hash;
Remove-Item -Recurse -Force不可恢复。
bash
powershell -NoProfile -Command '
$root="$env:LOCALAPPDATA\OpenAI\Codex\runtimes\cua_node"
$paths=@(
"$root\.staging-a708e72b10c27b59-28GhtX",
"$root\.staging-a708e72b10c27b59-Akbh1U",
"$root\.staging-a708e72b10c27b59-uzLtF9",
"$root\.staging-a708e72b10c27b59-Y6lZLs"
)
foreach($p in $paths){
if(Test-Path -LiteralPath $p){ Remove-Item -LiteralPath $p -Recurse -Force }
}
'
8. 最佳实践
- 区分"进程存在"和"窗口可用" :
MainWindowHandle=0不足以判断窗口不存在,Chromium 应用应使用EnumWindows验证。 - 更新后的首次启动要有耐心 :为
cua_node解压预留 3--10 分钟;期间只要文件数或 CPU 在增长,就不要结束进程。 - 用 staging 目录判断进度 :
runtimes\cua_node\.staging-*的文件数增长是"仍在工作"的直接证据。 - 只在确认无进展时采取重置:等待 15--20 分钟仍无变化,再考虑修复/重置或重装;重置会清除本地数据。
- 清理残留要精确 :只删除旧 hash 的
.staging-*,保留当前最终目录;避免通配符删除正在使用的运行时。 - 关注 MSIX 重定向与 EFS :包数据可能位于
<数据盘>:\WpSystem\<SID>,包文件可能带Encrypted属性;排查复制/启动慢时应检查这两点。 - 用事件日志区分挂起与崩溃 :
MoAppHang表示主线程无响应,不代表程序崩溃;结合启动阶段和窗口状态判断。 - 不要动无关进程 :VS Code 扩展的
codex.exe与桌面版 UI 相互独立。
9. 知识框架与排障图谱
9.1 知识框架
text
Codex 桌面版有进程无窗口
├─ 进程层
│ ├─ ChatGPT.exe 多进程:browser / renderer / gpu / crashpad
│ ├─ 排除 VS Code 扩展 codex.exe、sandbox service
│ └─ MainWindowHandle=0 不等于无窗口
├─ 窗口层
│ ├─ EnumWindows → Chrome_WidgetWin_0 / Chrome_WidgetWin_1
│ ├─ visible=False → 启动阶段尚未显示
│ └─ 屏幕外/最小化 → 另一类问题,可用 ShowWindow 恢复
├─ 启动层
│ ├─ MSIX 包 OpenAI.Codex
│ ├─ 入口 app/ChatGPT.exe(Chromium)
│ └─ 同步解压 cua_node 运行时
├─ 性能层
│ ├─ 2369 个文件 / 240.6 MB
│ ├─ EFS 加密解密开销
│ └─ 每次版本更新更换 hash,重新解压
├─ 证据层
│ ├─ .staging-* 目录与文件数增长
│ ├─ 日志 component_resolved copiedRuntimeLocation=true
│ └─ WER MoAppHang
└─ 处置层
├─ 等待解压完成(首选)
├─ 恢复窗口(ShowWindow SW_RESTORE)
├─ 修复/重置(后备)
└─ 清理旧 staging(精确路径)
9.2 排障决策树
text
启动后看不到窗口
├─ 任务管理器有 ChatGPT.exe?
│ ├─ 否 → 检查启动方式 / 安装状态 / AppID
│ └─ 是
│ ├─ EnumWindows 找到窗口但 visible=False?
│ │ ├─ 是 → 检查 runtimes\cua_node\.staging-*
│ │ │ ├─ 文件数/CPU 持续增长 → 等待完成(3--10 分钟)
│ │ │ └─ 长时间无变化 → 修复/重置或重装
│ │ └─ 否 → 检查窗口是否在屏幕外/其他虚拟桌面
│ └─ 事件日志有 MoAppHang?
│ └─ 是 → 结合启动阶段判断,不要直接判定为崩溃
10. 附录
A. 窗口枚举脚本
用于确认窗口是否存在、是否可见、标题和矩形:
bash
powershell -NoProfile -Command '
[Console]::OutputEncoding=[Text.Encoding]::UTF8
Add-Type @"
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
public class WinEnum {
public delegate bool Proc(IntPtr h, IntPtr l);
[DllImport("user32.dll")] public static extern bool EnumWindows(Proc cb, IntPtr l);
[DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr h, out uint pid);
[DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr h);
[DllImport("user32.dll")] public static extern int GetWindowText(IntPtr h, StringBuilder t, int m);
[DllImport("user32.dll")] public static extern int GetClassName(IntPtr h, StringBuilder c, int m);
[DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr h, out RECT r);
[StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left, Top, Right, Bottom; }
public static List<string> Run(uint[] pids) {
var set=new HashSet<uint>(pids); var o=new List<string>();
EnumWindows(delegate(IntPtr h, IntPtr l){
uint pid; GetWindowThreadProcessId(h,out pid); if(!set.Contains(pid)) return true;
var t=new StringBuilder(512); var c=new StringBuilder(256);
GetWindowText(h,t,t.Capacity); GetClassName(h,c,c.Capacity);
RECT r; GetWindowRect(h,out r);
o.Add(string.Format("pid={0} hwnd=0x{1:X} visible={2} title=[{3}] class=[{4}] rect=({5},{6})-({7},{8})",
pid,h.ToInt64(),IsWindowVisible(h),t,c,r.Left,r.Top,r.Right,r.Bottom));
return true;
}, IntPtr.Zero); return o;
}
}
"@
$pids=[uint32[]]@(Get-Process ChatGPT | Select-Object -ExpandProperty Id)
[WinEnum]::Run($pids)
'
恢复最小化窗口(hwnd 从上面的输出获取):
bash
powershell -NoProfile -Command '
Add-Type @"
using System; using System.Runtime.InteropServices;
public class WinRestore {
[DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr h,int c);
[DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr h);
}
"@
# 将 0x000000 替换为 EnumWindows 输出中的 hwnd
$h=[IntPtr]0x000000
[void][WinRestore]::ShowWindow($h,9)
[void][WinRestore]::SetForegroundWindow($h)
'
B. 时间线
| 时间 | 事件 |
|---|---|
| 15:53--15:57 | 旧版本多次启动,产生 4 个 a708... staging 残留 |
| 16:00:55 | 旧版本生成 MoAppHang 报告 |
| 16:07--16:15 | 应用更新到 26.1013.4894.0 |
| 16:15--16:20 | 新版本三次启动,均在解压接近完成时被终止 |
| 16:38:18 | 本次启动 |
| 16:41:04 | cua_node 解压完成(78fb63cda1a9e2b2,2369 文件 / 240.6 MB) |
| 16:41+ | 窗口显示;随后曾最小化,已恢复至前台 |
| 16:50 | 清理 4 个旧 staging,释放 745.4 MB;复核窗口与运行时正常 |
C. 术语
| 术语 | 说明 |
|---|---|
| MSIX | Windows 应用包格式,支持沙箱、重定向和受控安装 |
cua_node |
应用自带的计算机使用(Computer Use)运行时,包含 Node/Playwright 等组件 |
| EFS | Encrypting File System,Windows 文件级加密 |
MoAppHang |
WER 的"应用挂起"事件,表示主线程长时间无响应 |
| staging | 解压过程中的临时目录,完成后重命名为最终 hash 目录 |
D. 参考
- openai/codex #41170 --- First launch shows no window while extracting bundled cua_node runtime
- openai/codex #41289 --- App starts background processes but does not show a GUI window
- openai/codex #41551 --- Windows desktop app hangs before creating a window
- OpenAI Community --- ChatGPT Desktop launches background processes but no UI
免责声明:本文为个人排障记录,与 OpenAI 官方无关。文中命令仅用于诊断和清理临时目录;执行删除操作前请核对路径并做好备份。