<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title><![CDATA[Koen Lee — 中文]]></title>
    <link>https://www.koenlee.org/cn/</link>
    <description><![CDATA[Koen 写技术、商业和生活——写得慢，写给愿意坐下来想一想的人。]]></description>
    <language>zh-Hans</language>
    <atom:link href="https://www.koenlee.org/feed-cn.xml" rel="self" type="application/rss+xml"/>
    <lastBuildDate>Fri, 28 Aug 2026 21:30:00 -0700</lastBuildDate>
    <item>
      <title><![CDATA[我的 Claude Desktop 在 Windows 上反复自己关闭：最后靠换成 Squirrel 安装包解决]]></title>
      <link>https://www.koenlee.org/cn/technology/claude-desktop-windows-appx-bug.html</link>
      <guid isPermaLink="true">https://www.koenlee.org/cn/technology/claude-desktop-windows-appx-bug.html</guid>
      <pubDate>Fri, 28 Aug 2026 21:30:00 -0700</pubDate>
      <description><![CDATA[报错说文件被占用，但根本没有进程占用它。从「另一程序正在使用此文件」一路查到 AppX 容器的句柄泄漏，最后靠换回 Squirrel 安装包绕开这个 bug。]]></description>
      <content:encoded><![CDATA[<p>我叫 Koen，写代码写了将近 30 年，现在在洛杉矶做独立开发，客户项目和自己的产品都在做。这篇文章记录的是最近几天我在 Windows 上折腾 Claude Desktop 的完整过程——从一个看起来很普通的错误弹窗，到最后动用 Windows 内核对象、Job Object、AppX 容器这些底层知识才勉强绕过去。写出来，是因为我在排查过程中翻遍了 GitHub 上一堆 issue，发现遇到这个问题的人不少，但大部分讨论都零散、不完整，没人把整个决策链条讲清楚。希望这篇能帮后来的人少走弯路。</p>
<p>先把结论放在最前面：<strong>这是 Claude Desktop 在 Windows 上因为使用 MSIX 打包方式导致的一个已知未修复 bug，官方目前没有根治方案。我最后的解决办法是彻底放弃 MSIX 版本，改成官方仍在并行提供的传统 Squirrel（.exe）安装包，绕开触发这个 bug 的那套容器机制，才算恢复正常。</strong> 下面是完整过程，包括我踩过的坑和试过没用的方案，都如实记录。</p>
<h2>先说一下我的使用习惯，这很关键</h2>
<p>我的工作电脑常年不关机——这是我从业这么多年养成的习惯，也是刻意的选择。桌面上常年开着一堆终端、编辑器、后台服务、SSH 会话，很多是跨天甚至跨周持续运行的工作进程。我最讨厌的一件事就是 Windows 自作主张地自动更新、自动重启，每次都打断这些常驻进程，之后还得一个个手动拉起来，非常影响效率。所以我的机器上自动重启是关掉的，Claude Desktop 也是长期开着不关，跟其他工作窗口一样常驻在后台。</p>
<p>这个使用习惯直接决定了这次问题对我的影响方式，也决定了我为什么从一开始就排斥"重启电脑"这个方案——这不是因为我懒得重启，而是重启这件事对我来说代价很高：一堆需要手动恢复的进程和上下文，从一个老程序员的思维习惯来说，<strong>指望靠"重启大法"日常维持一个软件能用，是一件很反人类的事情</strong>。软件应该是可预期、可维护的，不应该需要用户靠玄学手段维持稳定。</p>
<h2>问题是怎么被发现的</h2>
<p>第一次出问题，是某天早上起床后走到电脑前，发现 Claude Desktop 的窗口已经不在了——它在我睡觉的这几个小时里自己关闭了，大概率是触发了一次后台自动更新。我手动点击图标想重新打开，跳出来一个 Windows 系统级的错误框：</p>
<pre><code>另一程序正在使用此文件。
C:\Program Files\WindowsApps\Claude_1.37937.3.0_...</code></pre>
<p>一个"确定"按钮，点了之后应用直接不启动。第一反应是某个进程占着文件没释放，干了这么多年这种问题见得多了，正常思路是：</p>
<ol>
<li>任务管理器杀掉所有 Claude 相关进程</li>
<li>重新打开</li>
</ol>
<p>试了，没用，同样的弹窗。既然报错里明确写了路径 <code>C:\Program Files\WindowsApps\Claude_1.37937.3.0_...</code>，我第一反应是自己去这个目录看看，到底是什么文件被占用了。结果打开资源管理器，在 <code>C:\Program Files</code> 底下压根找不到 <code>WindowsApps</code> 这个文件夹——凭经验知道它一定是被隐藏了，索性直接在地址栏里手打路径回车。</p>
<p>这下更离谱了：<strong>资源管理器直接弹窗说我没有权限访问这个文件夹</strong>。我当时是真的愣住了——我用的是管理员账户，本机最高权限，一个系统自带的路径，居然连"看一眼"的权限都没有。这跟我几十年积累的直觉完全不符：在我的认知里，管理员理应能访问本机上的任何路径，顶多是操作时弹个 UAC 确认。</p>
<p>带着这个疑惑我顺手查了一下 <code>WindowsApps</code> 这个文件夹到底是什么来路，发现它确实不简单：这个目录里存放的是所有通过 Microsoft Store / AppX 机制安装的应用包，它的<strong>所有者不是 Administrators 组，而是一个叫 <code>TrustedInstaller</code> 的特殊系统服务账户</strong>——权限层级上比本机管理员还要高。默认情况下，哪怕是管理员账户，对这个目录也只有极其有限的访问权限，微软这么设计是为了防止用户（或者恶意软件）手贱直接改动或删除商店应用的包文件，破坏应用的完整性校验。想真正进去看内容，得手动去"属性 → 安全 → 高级"里把所有者从 <code>TrustedInstaller</code> 改成自己的账户，而且社区里明确警告，改了之后如果不小心动了里面的文件，可能会连带搞坏商店应用的正常运作，事后最好再把所有者改回 <code>TrustedInstaller</code>。</p>
<p>这个发现在当时给我一个提醒：<strong>Claude 被装在了一个我作为管理员都无法随意触碰的特殊受保护区域里</strong>，这已经不是普通 Win32 程序"装在 Program Files 底下"那种朴素的文件系统关系了，背后一定有一套我还不了解的机制在管着它。这也是促使我后来认真去查 MSIX/AppX 这套体系的一个直接契机。</p>
<p>当时查到的零星资料大多是"重启电脑试试"，考虑到我并不想为了一个应用去打断电脑上一堆常驻的工作进程，我先按上面这个思路自己摸索了一阵，但一时没查出结果，时间也紧，最后还是妥协重启了一次电脑。重启后问题消失，我没多想，以为是一次偶发的冲突。</p>
<h2>问题复发：这次我下定决心不再靠重启解决</h2>
<p>没过几天，同样的情况又出现了——早上过去看，Claude 又自己关掉了，同样的报错弹窗。这次我意识到：<strong>如果这个问题会反复出现，那"重启电脑"根本不是解决方案，只是一个治标不治本、而且代价很高的临时创可贴</strong>。对我这种电脑常年不关机、桌面上一堆工作进程的使用方式来说，靠定期重启来维持一个应用能打开，是完全不能接受的。这也是我这次决定彻底查清楚根因、动手系统排查的直接原因。</p>
<h2>排查过程：一层一层往下扒</h2>
<h3>第一层：以为是 MSIX 更新时的文件锁</h3>
<p>先查到的信息是，Claude Desktop 在 Windows 上是通过 <strong>MSIX 包</strong>分发的，错误路径 <code>C:\Program Files\WindowsApps\Claude_...</code> 也印证了这一点。MSIX 是微软的现代应用打包格式，更新时理论上要先完全退出旧版本才能替换文件。GitHub 上 anthropics/claude-code 仓库确实有对应的 issue（#66497）在报告"Relaunch to Update"点了之后卡在这个报错上，说是旧进程没释放文件锁。</p>
<p>按这个思路，我在管理员 PowerShell 里彻底清理进程：</p>
<pre><code class="language-powershell">Get-Process | Where-Object { $_.ProcessName -match "claude|cowork" } | Stop-Process -Force</code></pre>
<p>没用。而且我发现有个细节：任务管理器里根本没有任何进程在占用这个安装目录下的文件——这个"文件被占用"的提示本身就有问题。</p>
<h3>第二层：真正的元凶是 AppX 容器层，不是文件锁</h3>
<p>继续往下查，找到了几个更硬核的 issue（#53247、#61635、#73107），里面有开发者动用了 Sysinternals 的 <code>handle.exe</code> 和 Process Explorer 去实测——<strong>确认没有任何用户空间进程锁定这个文件</strong>。真正的故障发生在 Windows 的 <strong>AppX/Desktop Bridge 容器层</strong>：Windows 事件查看器里能看到：</p>
<pre><code>Event ID 215: 0x80070020: cannot create the Desktop AppX container for package Claude_&lt;版本&gt;_x64__pzs8sxrjxfjjc due to an error converting the job
Event ID 208: 0x80070020: cannot create the process for package ... due to an error setting up the runtime</code></pre>
<p><code>0x80070020</code> = <code>ERROR_SHARING_VIOLATION</code>，出现在 <strong>Job Object 转 Silo（容器）</strong> 这一步。也就是说，Claude 这个应用被打包进了一个隔离容器里运行（这是 MSIX/Desktop Bridge 的标准做法），每次启动都要创建一个专属容器，而这次创建失败了，Windows 把这个失败硬套上了"文件被占用"这句通用提示——<strong>这句话是错的，具有很强的误导性</strong>，我一开始按"文件锁"的思路排查完全是被这句话带偏了。</p>
<p>这对我来说是这次排障里最有价值的一个认知：<strong>系统弹窗给出的错误文案，未必是真实的故障层级，尤其是跨了几层抽象（AppX 容器 → Win32 错误码 → 通用提示文案）之后，信息损耗很严重。真正定位问题，得看事件查看器里的原始 Event ID，而不是弹窗文字。</strong></p>
<h3>第三层：内核对象的引用计数，以及谁在钉着它不放</h3>
<p>再往下查，issue #61635 里有个开发者给出了完整的技术链条，这也是这次让我觉得最长见识的部分：</p>
<p>Windows 的内核对象（Job Object 也是其中一种）是<strong>引用计数</strong>的——只要还有任何进程或服务持有它的句柄，这个对象就不会被销毁，哪怕它已经从对象管理器的命名空间里"消失"了。这次的具体情况是：</p>
<ul>
<li>Claude 的应用容器命名是固定规则，比如 <code>Container_Claude_&lt;版本号&gt;_&lt;用户SID&gt;</code></li>
<li>之前我用 Claude 触发过需要 UAC 提权的操作，Windows 负责处理提权弹窗的 <strong>AppInfo（Application Information）服务</strong>打开了一个指向这个容器 Job 对象的句柄</li>
<li>Claude 退出、或者应用更新到新版本之后，<strong>AppInfo 服务从来没有释放这个句柄</strong>——一个典型的句柄泄漏</li>
<li>于是这个"僵尸"对象靠着 AppInfo 那一个引用一直活在内核里，占着命名槽位</li>
<li>下次启动想创建同名新容器时，跟这个还没死透的旧对象撞名，触发 <code>ERROR_SHARING_VIOLATION</code></li>
</ul>
<p>重启电脑之所以有效，是因为重启会强制终止所有进程和服务，句柄归零，内核才真正把这个僵尸对象销毁、腾出槽位。但正如前面说的，重启对我来说代价太高，我需要一个不用重启的办法。</p>
<h3>尝试绕开重启：几次不成功的努力</h3>
<p>搞清楚原理之后，我试了几个方向，都是冲着"不碰重启、不碰登出"这个前提去的：</p>
<p><strong>尝试 1：单独重启 AppInfo 服务</strong>（既然是它持有的泄漏句柄，直接重启它理论上该有等价效果）：</p>
<pre><code class="language-powershell">Restart-Service -Name Appinfo -Force</code></pre>
<p>结果：</p>
<pre><code>Restart-Service : 服务"Application Information (Appinfo)"停止失败。</code></pre>
<p>后来才明白，<strong>AppInfo 这个服务被 Windows 设计成不允许手动停止</strong>——它本身是"允许其他东西提权"的守门人，如果能被随手停掉，会陷入"想停 AppInfo 得先提权、但提权又依赖 AppInfo"的死锁。论坛上能看到不少人误禁用它之后，整台电脑所有需要管理员权限的操作全部失效，得进安全模式才能救回来。这条路彻底堵死。</p>
<p><strong>尝试 2：重启 AppXSVC / ClipSVC</strong>（更通用的 AppX 部署服务）：</p>
<pre><code class="language-powershell">Restart-Service -Name AppXSVC -Force
Restart-Service -Name ClipSVC -Force</code></pre>
<p>这个能执行，但没解决问题——因为这次的根因是 Claude 应用自己在某次崩溃/更新时没清理好容器状态，不是这两个通用服务本身出了故障，其他 AppX 应用（记事本、OneDrive、Store 应用）在同一时间窗口都能正常创建容器，说明系统级服务本身没坏。</p>
<p><strong>尝试 3：注销 Windows 账户而不是重启</strong>——社区里有反馈说注销效果等同重启，因为很多会话级状态会跟着清空。但对我来说，我电脑上常年开着一堆工作窗口和进程，注销的代价跟重启没什么本质区别，一样要手动恢复一堆上下文，这条路对我不现实，直接放弃。</p>
<p>到这一步，我确认了：<strong>这是 Anthropic 需要在代码层面修的 bug（在启动/崩溃路径上没有可靠地释放容器句柄），社区目前没有能在系统层面安全绕开的手段</strong>，GitHub 上对应的核心 issue（#53247）截至我写这篇文章时仍然是 Open 状态。</p>
<h2>换个思路：既然是 MSIX 容器机制的锅，那就不用 MSIX</h2>
<p>想明白这一层之后，思路变了——与其在系统层面跟这个 bug 纠缠，不如换一个根本不会触发这套容器机制的安装方式，从源头避开问题，而不是每次发作了再去救火。</p>
<p>查证后发现：Claude Desktop 在 Windows 上其实有两条并行的分发线：</p>
<ul>
<li><strong>.msix</strong>：2026 年 2 月上线 Cowork 功能时切换过来的"现代"安装方式，带 Cowork 功能，但也带着这套容器机制和它的 bug</li>
<li><strong>.exe（Squirrel 框架）</strong>：更早期、更传统的安装方式，不走 AppX 容器，但不支持 Cowork</li>
</ul>
<p>我不用 Cowork，只用 Claude Code 做日常开发辅助，这两者互不依赖，所以换成 Squirrel 版本对我来说没有任何功能损失。</p>
<h3>第一次尝试：winget，卡住不动</h3>
<pre><code class="language-powershell">winget install --id Anthropic.Claude -e</code></pre>
<p>跑了很久没有任何输出，怀疑是隐藏的协议确认提示卡住了，加参数重试：</p>
<pre><code class="language-powershell">winget install --id Anthropic.Claude -e --accept-package-agreements --accept-source-agreements</code></pre>
<p>依然没反应。排查了 <code>winget source reset --force</code>、检查有没有残留的 winget 进程、有没有待处理的系统更新——都没解决，最后放弃 winget，直接找官方的直链。</p>
<h3>第二次尝试：直链下载，撞上 Cloudflare 人机验证</h3>
<p>找到官方的 Squirrel 版直链重定向地址：</p>
<pre><code>https://claude.ai/api/desktop/win32/x64/exe/latest/redirect</code></pre>
<p>用命令行工具直接拉取：</p>
<pre><code class="language-powershell">Invoke-WebRequest -Uri "https://claude.ai/api/desktop/win32/x64/exe/latest/redirect" -OutFile "$env:USERPROFILE\Downloads\ClaudeSetup.exe"</code></pre>
<p>返回的不是安装包，是一段 Cloudflare 人机验证挑战页面的 HTML/JS——命令行工具没有 JS 引擎，过不了这层验证。</p>
<h3>最终方案：直接用浏览器打开这个链接</h3>
<p>放弃命令行，把这个链接直接粘贴到浏览器地址栏，浏览器正常完成人机验证，触发下载，拿到真正的安装包。双击运行，静默安装，<strong>全程没有弹出 UAC 提权框</strong>——这也侧面印证了它走的确实是不需要容器化、不需要额外提权的传统 Squirrel 路径。</p>
<p>验证安装结果：</p>
<pre><code class="language-powershell">Get-ChildItem "$env:LOCALAPPDATA" -Filter "*claude*" -ErrorAction SilentlyContinue
Get-ChildItem "$env:LOCALAPPDATA" -Filter "*anthropic*" -ErrorAction SilentlyContinue</code></pre>
<p>确认落在 <code>%LOCALAPPDATA%\AnthropicClaude</code> 下面，是 Squirrel 结构。</p>
<h2>终于能用了，而且这次不用重启</h2>
<p>换成 Squirrel 版本之后，Claude Desktop 终于正常打开了——这次没有靠重启解决，是从安装方式的根源上绕开了那套容器机制。对我这种不喜欢重启、电脑常年挂着一堆工作进程的使用方式来说，这才是真正意义上"解决"了问题，而不是又一次靠运气把故障暂时压下去。</p>
<p>登录进去，普通聊天记录完好——这个数据本来就存在服务器端，跟本地装什么版本没关系。</p>
<p>但打开 Claude Code 相关的历史会话时，看到的是这样的提示：</p>
<pre><code>Can't reach your computer
It may be asleep or offline. This session will reconnect when it's back.
Remote Control host unreachable (computer_unreachable)</code></pre>
<p>这是 Claude Code 的 <strong>Remote Control（远程控制）</strong>功能——它允许你从手机或网页接续电脑上正在跑的本地会话，但前提是<strong>电脑上那个具体的进程要一直活着</strong>。这几天我反复折腾（卸载、重装、还有中间那次不情愿的重启），原来承载会话的那个本地进程早就被终止过好几次了，会话记录还在（历史数据没丢），但作为"远程宿主"的连接已经彻底断了，不会自动恢复——这不是网络问题，是压根没有活的进程可连。</p>
<p>想继续用，得回到电脑上用 <code>claude --resume</code> 重新接上历史会话，如果还要用远程控制，得在新起的会话里重新执行一次 <code>/remote-control</code>，建立新的远程连接。这算是这次折腾里一个没能完全避免的代价，记在这里供大家心理有数。</p>
<h2>写给同样遇到这个问题的你</h2>
<p>如果你也在 Windows 上碰到"另一程序正在使用此文件"这个报错，指向 <code>C:\Program Files\WindowsApps\Claude_...</code>，这是我总结下来的建议顺序：</p>
<ol>
<li><strong>先别急着重启</strong>，试试杀掉所有 Claude/cowork 相关进程后重新打开，小概率能解决</li>
<li><strong>如果你之前在 Claude 里做过需要提权的操作</strong>，去任务管理器"详细信息"页找找有没有异常的孤儿子进程，杀掉可能有效，不用碰重启</li>
<li><strong>如果你不需要 Cowork，只用 Claude Code 或普通聊天</strong>，直接换成 Squirrel 版本是目前最彻底的绕开方案：卸载 MSIX 版本 → 浏览器打开 <code>https://claude.ai/api/desktop/win32/x64/exe/latest/redirect</code> → 手动下载安装</li>
<li><strong>换版本前记得备份</strong> <code>%USERPROFILE%\.claude\projects\</code> 这个目录，虽然理论上重装不会碰它，但多一层保险总没错</li>
<li><strong>心理预期要放对</strong>：这是 Anthropic 那边的产品 bug，不是你的系统或操作有问题，也没有你能在自己电脑上"根治"的办法，能做的只是绕开触发条件</li>
<li><strong>如果你跟我一样电脑常年不关机、讨厌自动重启</strong>，那这次的经验对你会特别有参考价值——靠重启解决反复发作的问题，从来不是真正的解决方案，找到根因、换一条不触发 bug 的路径，才是值得花时间的方向</li>
<li>如果你依赖 Remote Control 远程接续 Code 会话，注意这类系统层面的折腾（重启、重装）都会打断当前的远程连接，回到电脑本地手动 <code>resume</code> 就行，数据不会丢</li>
</ol>
<p>写了 30 年代码，这种问题最打击人的地方从来不是解决它本身有多难，而是"系统给你的错误提示是错的"——你得先自己证伪表面的说法，才能找到真正值得深挖的方向。这次从"文件占用"查到"内核对象引用计数"，绕了不少路，但每一层查下去都有真实的技术依据可以验证，这也是我愿意花时间写这篇文章的原因：希望能帮后面遇到同样问题的人，跳过我踩过的那几个弯路，也少一次不必要的重启。</p>]]></content:encoded>
    </item>
  </channel>
</rss>
