适用症状:Docker Desktop 引擎起不来、
wsl --import报错或挂起、WSL 升级 MSI 失败、wslservice.exe卡 START_PENDING、Chrome 数据库异常清空。环境:Windows 11 + Docker Desktop 4.83 + WSL 3.0.1。全部方案均为本人实测(2026-10,3 个月排障实录),按症状索引,直接对号入座。
症状速查表
| 症状 | 真实原因 | 跳转 |
|---|---|---|
Wsl/.../HCS/0x80070570 导入发行版失败 |
vhd/文件系统损坏 | [§1](#症状 真实原因 跳转 Wsl/.../HCS/0x80070570 导入发行版失败 vhd/文件系统损坏 §1 WslService 卡 START_PENDING,CPU=0 Lxss 注册表幽灵发行版键 §2 WSL MSI 安装卡死在最后一步 DeprovisionMsix + AppX 栈僵死 §3 MSI 报 1603/1619 布局问题/事务残留 §4 浏览器登录态反复丢失、SQLite 库表为空 文件系统层损坏 §5 引擎修好了但 pull 不动镜像 直连 registry 被墙 §6 "#1-0x80070570%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E6%8D%9F%E5%9D%8F") |
WslService 卡 START_PENDING,CPU=0 |
Lxss 注册表幽灵发行版键 | [§2](#症状 真实原因 跳转 Wsl/.../HCS/0x80070570 导入发行版失败 vhd/文件系统损坏 §1 WslService 卡 START_PENDING,CPU=0 Lxss 注册表幽灵发行版键 §2 WSL MSI 安装卡死在最后一步 DeprovisionMsix + AppX 栈僵死 §3 MSI 报 1603/1619 布局问题/事务残留 §4 浏览器登录态反复丢失、SQLite 库表为空 文件系统层损坏 §5 引擎修好了但 pull 不动镜像 直连 registry 被墙 §6 "#2-wslservice-%E5%8D%A1-start_pending") |
| WSL MSI 安装卡死在最后一步 | DeprovisionMsix + AppX 栈僵死 | [§3](#症状 真实原因 跳转 Wsl/.../HCS/0x80070570 导入发行版失败 vhd/文件系统损坏 §1 WslService 卡 START_PENDING,CPU=0 Lxss 注册表幽灵发行版键 §2 WSL MSI 安装卡死在最后一步 DeprovisionMsix + AppX 栈僵死 §3 MSI 报 1603/1619 布局问题/事务残留 §4 浏览器登录态反复丢失、SQLite 库表为空 文件系统层损坏 §5 引擎修好了但 pull 不动镜像 直连 registry 被墙 §6 "#3-msi-%E5%8D%A1%E6%AD%BB-deprovisionmsix") |
| MSI 报 1603/1619 | 布局问题/事务残留 | [§4](#症状 真实原因 跳转 Wsl/.../HCS/0x80070570 导入发行版失败 vhd/文件系统损坏 §1 WslService 卡 START_PENDING,CPU=0 Lxss 注册表幽灵发行版键 §2 WSL MSI 安装卡死在最后一步 DeprovisionMsix + AppX 栈僵死 §3 MSI 报 1603/1619 布局问题/事务残留 §4 浏览器登录态反复丢失、SQLite 库表为空 文件系统层损坏 §5 引擎修好了但 pull 不动镜像 直连 registry 被墙 §6 "#4-msi-1603--1619") |
| 浏览器登录态反复丢失、SQLite 库表为空 | 文件系统层损坏 | [§5](#症状 真实原因 跳转 Wsl/.../HCS/0x80070570 导入发行版失败 vhd/文件系统损坏 §1 WslService 卡 START_PENDING,CPU=0 Lxss 注册表幽灵发行版键 §2 WSL MSI 安装卡死在最后一步 DeprovisionMsix + AppX 栈僵死 §3 MSI 报 1603/1619 布局问题/事务残留 §4 浏览器登录态反复丢失、SQLite 库表为空 文件系统层损坏 §5 引擎修好了但 pull 不动镜像 直连 registry 被墙 §6 "#5-chrome-sqlite-%E5%BA%93%E5%85%A8%E7%A9%BA%E4%B8%8E-cookie-%E8%BF%81%E7%A7%BB") |
| 引擎修好了但 pull 不动镜像 | 直连 registry 被墙 | [§6](#症状 真实原因 跳转 Wsl/.../HCS/0x80070570 导入发行版失败 vhd/文件系统损坏 §1 WslService 卡 START_PENDING,CPU=0 Lxss 注册表幽灵发行版键 §2 WSL MSI 安装卡死在最后一步 DeprovisionMsix + AppX 栈僵死 §3 MSI 报 1603/1619 布局问题/事务残留 §4 浏览器登录态反复丢失、SQLite 库表为空 文件系统层损坏 §5 引擎修好了但 pull 不动镜像 直连 registry 被墙 §6 "#6-%E9%95%9C%E5%83%8F%E6%8B%89%E5%8F%96%E5%8A%A0%E9%80%9F") |
1. 0x80070570(文件系统损坏)
现象:Docker 后端日志出现:
arduino
wsl.exe --import-in-place docker-desktop ... failed:
文件或目录损坏且无法读取。
错误代码: Wsl/Service/RegisterDistro/CreateVm/HCS/0x80070570
定位:0x80070570 = ERROR_FILE_CORRUPT。vhd/vhdx 或其所在卷的文件系统损坏。
修复:
makefile
:: 1. 管理员 PowerShell,预约开机自检(C 盘无法在线锁卷)
chkdsk C: /f
:: 按提示输入 Y,重启。观察关键字段:
:: "0 KB in bad sectors" → 硬件无损,纯文件系统层损坏(可放心)
:: 有坏簇数字 → 立即备份数据,考虑换盘
:: 2. 用安装包内的干净模板替换损坏的 vhdx
copy /Y "C:\Program Files\Docker\Docker\resources\wsl\ext4.vhdx" ^
"%LOCALAPPDATA%\Docker\wsl\main\ext4.vhdx"
:: 3. SMART 佐证(硬件健康与否)
powershell "Get-PhysicalDisk | Format-Table FriendlyName,HealthStatus"
注意:chkdsk 通过 ≠ 万事大吉。文件系统损坏往往是连环事故的第一张多米诺,后续 WSL/浏览器异常接着查。
2. WslService 卡 START_PENDING
现象 :sc query WslService 显示 STATE : 2 START_PENDING 数小时不变;服务进程 CPU=0;wsl.exe 任何命令挂起 45s+。
排查顺序(本人实测有效的优先级):
bash
# ① 依赖服务
Get-Service vmcompute, hns # 必须 Running
# ② 插件目录
dir "C:\Program Files\WSL\lib" # 只有 3 个 GPU dll 是正常的
# ③ ★幽灵发行版注册键(本例真凶)★
reg query "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss" /s
# 逐个核对每个 GUID 键的 BasePath + VhdFileName 指向的文件是否存在
修复 :删掉指向不存在文件的发行版键(服务卡死时 wsl --unregister 无效,只能删注册表):
sql
reg delete "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss{GUID}" /f
taskkill /F /IM wslservice.exe
sc start WslService
效果 :wsl --version 从 45s 超时 → 131ms。
原理:WSL 是"文件 + 注册表"双状态组件。vhd 文件删了但注册表键残留,服务枚举发行版时撞上幽灵键即挂死。凡是手动删过 vhd / 重装过 Docker 的机器都要查这一步。
3. MSI 卡死(DeprovisionMsix)
现象 :wsl --update 或手动运行 WSL 的 msi,进度条卡在最后一步,verbose log 尾部永远是:
ini
ActionStart(Name=DeprovisionMsix,,)
CustomActionSchedule(Action=DeprovisionMsix,ActionType=3073,...)
原因:该自定义动作要反预配(deprovision)收件箱版 WSL 的 MSIX 包,但机器的 AppX 部署栈僵死------验证方法(会挂起即中招):
bash
Get-AppxPackage -AllUsers -Name "*WindowsSubsystem*" # 挂起 = AppX 栈坏
修复(三层递进) :
bash
:: 第 1 层:DISM 移除预配包(走独立部署路径,绕开僵死的 AppXSvc)
dism /online /Get-ProvisionedAppxPackages | findstr /i "Subsystem"
dism /online /Remove-ProvisionedAppxPackage ^
/PackageName:MicrosoftCorporationII.WindowsSubsystemForLinux_<版本>_x64__8wekyb3d8bbwe
:: 第 2 层:每用户注册的包用 DISM 删不掉 → 干净启动
:: msconfig → 服务 → 勾选"隐藏所有 Microsoft 服务" → 全部禁用 → 重启
:: 第 3 层:干净启动下重跑 MSI,一次通过
msiexec /i "%TEMP%\wsl.3.0.1.0.x64.msi" /l*v "%TEMP%\wsl_install.log"
警告:干净启动会禁用全部第三方服务。已知副作用:浏览器登录态异常(见 §5)。修完后 msconfig → 常规 → 正常启动 → 再重启恢复。
4. MSI 1603 / 1619
1603 布局陷阱(从管理解包/缓存目录拿 MSI 装时必踩):
csharp
MSI (s): Source for file 'RdpWinStlHelper.dll' is uncompressed, at '...\PFiles64\WSL'
MSI (s): Folder is not accessible: C:\Users...\Downloads\PFiles64
此类 MSI 的文件不内嵌 CAB,MSI 必须与 PFiles64\WSL 文件夹同级才能安装。单独复制 MSI 到任意目录运行必然 1603。
1619 = 安装包路径无效 :卸载过程会删除 C:\Windows\Installer 下的缓存 MSI。卸载+重装分两步跑时,第二步会找不到包。对策:先备份缓存 MSI,或从官方 release 重下。
通用诊断命令(GUI 永远只说 1603,真相在日志里):
bash
msiexec /i xxx.msi /l*v full.log
:: 在日志里搜(按优先级):
:: "return value 3" → 第一个失败点,看它前 30 行
:: "Note: 1: 1714/1723/1603"
:: "DEBUG: Error"
挂起的事务清理:强杀 msiexec 后安装器报"当前安装处于挂起状态 (0x80070644)":
makefile
reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\InProgress"
:: 有值则删,或重启(SCM 会回收)
5. Chrome SQLite 库全空与 Cookie 迁移
现象:所有网站登录态丢失;重新登录后重启浏览器即掉。特征性诊断结果:
| 检查项 | 正常值 | 受损值 |
|---|---|---|
| 文件头 | SQLite format 3 |
SQLite format 3(照样合法!) |
| sqlite_master 表数量 | 十几个 | 0 |
| 非零字节占比 | >60% | ~9% |
文件头合法 + 表结构清零 → Chrome 打开库认为正常,但写入全部落空 → 登录仅存内存 → 关浏览器即蒸发。这就是"怎么登都掉"的机理。
恢复决策树:
go
① 卷影副本恢复?
vssadmin list shadows(管理员)
copy "\?\GLOBALROOT\Device\HarddiskVolumeShadowCopyN\Users<user>...\Cookies" dst
→ 注意验证快照版本的表结构,文件系统损坏往往在快照前已发生
② 其他 Chromium 浏览器还有登录态?
→ Cookie 迁移(下述,本人验证可行的完整路径)
③ 都没有 → 只能账号找回
Cookie 迁移(Firefox → Chrome) :
sql
# Step 1: 提取(Firefox 运行中库被锁,复制 + wal 即可读)
Copy-Item "$env:APPDATA\Mozilla\Firefox\Profiles<profile>\cookies.sqlite" D:\ff.db
python -c "import sqlite3; con=sqlite3.connect(r'D:\ff.db'); print(con.execute(
"SELECT name,value FROM moz_cookies WHERE host LIKE '%bilibili%'").fetchall())"
# Step 2: Chrome 新版有 App-Bound 加密(Local State 密钥前缀 DPA),
# 直接写 Cookies 数据库的明文列会在启动时被清空 → 必须走 CDP 注入
python
# Step 3: CDP 注入(完整可运行,Python + websockets)
import subprocess, time, json, urllib.request, asyncio, websockets
CHROME = r"C:\Program Files\Google\Chrome\Application\chrome.exe"
# 关键:默认 User Data 目录禁止开调试端口,必须复制一份副本配置
UD = r"C:\Temp\chrome_cdp"
COOKIES = json.load(open("bili_cookies.json")) # 含 domain/name/value/path/secure/httpOnly/expires
subprocess.Popen([CHROME, "--remote-debugging-port=9222", f"--user-data-dir={UD}",
"--no-first-run", "about:blank"])
for _ in range(15):
time.sleep(2)
try:
targets = json.loads(urllib.request.urlopen(
"http://127.0.0.1:9222/json/list", timeout=2).read()); break
except Exception: pass
page = next(t for t in targets if t["type"] == "page")
async def main():
async with websockets.connect(page["webSocketDebuggerUrl"], max_size=10**7) as ws:
mid = 0
async def cmd(m, p):
nonlocal mid; mid += 1
await ws.send(json.dumps({"id": mid, "method": m, "params": p}))
while True:
r = json.loads(await ws.recv())
if r.get("id") == mid: return r
await cmd("Network.enable", {})
for c in COOKIES:
p = {"name": c["name"], "value": c["value"], "domain": c["domain"],
"path": c["path"], "secure": c["secure"], "httpOnly": c["httpOnly"]}
if c.get("expires"): p["expires"] = c["expires"] / 1e6 # Chrome us → CDP s
await cmd("Network.setCookie", p)
# 验证(真实业务接口,落盘前最后防线)
await cmd("Page.navigate", {"url": "https://www.bilibili.com"})
ev = await cmd("Runtime.evaluate", {"expression":
"fetch('https://api.bilibili.com/x/web-interface/nav',{credentials:'include'})"
".then(r=>r.json()).then(j=>j.data.isLogin?'LOGGED_IN':'FAIL')",
"awaitPromise": True, "returnByValue": True})
print(ev["result"]["result"]["value"])
asyncio.run(main())
r
# Step 4: ★优雅关闭(绝不能 taskkill /F!强杀不触发 Cookie 落盘)
(Get-Process chrome | ForEach-Object { $_.CloseMainWindow() }) ; Start-Sleep 6
# Step 5: 把临时配置的登录态拷回真实配置(Chrome 必须已完全退出)
Copy-Item "C:\Temp\chrome_cdp\Default\Network\Cookies" `
"$env:LOCALAPPDATA\Google\Chrome\User Data\Default\Network\Cookies" -Force
Copy-Item "C:\Temp\chrome_cdp\Local State" `
"$env:LOCALAPPDATA\Google\Chrome\User Data\Local State" -Force
验证 :重启 Chrome,F12 → Application → Cookies 确认 SESSDATA 存在;关开浏览器不掉即成功。
6. 镜像拉取加速
引擎就绪后 docker pull 超时的最后一块拼图------直连 registry 被墙。编辑 %USERPROFILE%.docker\daemon.json:
json
{
"builder": { "gc": { "defaultKeepStorage": "20GB", "enabled": true } },
"experimental": false,
"registry-mirrors": [
"https://docker.1ms.run",
"https://docker.m.daocloud.io",
"https://dockerproxy.net"
]
}
重启 Docker Desktop 后验证:
arduino
docker info | findstr -A 4 "Registry"
docker pull hello-world → Status: Downloaded newer image
附:完整修复时序(可直接照抄的操作顺序)
markdown
1. chkdsk C: /f → 重启自检 → 确认 0 bad sectors
2. reg query HKCU...\Lxss /s → 删除指向不存在文件的发行版键
3. wsl --unregister docker-desktop && 用安装包模板重新导入
4. wsl -d <distro> echo BOOT_OK ← 209ms 内返回才算服务健康
5. 启动 Docker Desktop → docker info 看到 Server 段即成功
6. daemon.json 加 registry-mirrors → docker pull 验证
血泪经验(全文浓缩)
- GUI 错误码一文不值,verbose log 才是真相 (MSI
/l*v、Dockercom.docker.backend.exe.log) - WSL/MSI 这类"文件+注册表"双状态组件,挂死先查注册表残留
- 落盘判成功 :CDP success ≠ 数据在磁盘;
taskkill /F是 Cookie 消失的头号元凶 - 文件系统损坏是连环事故:vhd、SQLite 库会接连中招,修复后必须全盘体检
- 干净启动是大杀器也是双刃剑:修好了部署栈,丢了浏览器登录态------用前关浏览器,用后备好账号密码
本文所有命令在 Windows 11 24H2 + Docker Desktop 4.83 + WSL 3.0.1 实测通过。转载请注明出处。
本文全部脚本已开源 :github.com/TrueFurina/... (sqlite 损坏检测 / VSS 提取 / Firefox Cookie 提取 / CDP 注入 / 优雅落盘,觉得有用点个 Star ⭐)
附录 A:SQLite 结构性损坏检测脚本
§5 场景的快速定性工具------判断任意 SQLite 文件是"真损坏"还是"表结构清零"(两者处置完全不同):
python
# sqlite_check.py
import sqlite3, sys
def check(path):
with open(path, "rb") as f:
head = f.read(65536)
ok_header = head[:16] == b'SQLite format 3\x00'
nonzero = sum(1 for b in head if b) / len(head)
print(f"文件头: {head[:16]} 合法={ok_header}")
print(f"前64K非零字节占比: {nonzero:.1%} (<30% 高度可疑)")
try:
con = sqlite3.connect(path)
tables = [r[0] for r in con.execute(
"SELECT name FROM sqlite_master WHERE type='table'")]
print(f"表: {tables}")
except Exception as e:
print(f"打开失败: {e}")
return False
return ok_header and len(tables) > 0
for p in sys.argv[1:]:
print(f"\n=== {p} ===")
print("健康" if check(p) else "★结构性损坏(表清零)★")
判读矩阵:
| 文件头 | 表数量 | 结论 | 处置 |
|---|---|---|---|
| 合法 | >0 | 健康 | 正常使用 |
| 合法 | 0 | 结构性损坏(本案例) | 移除重建,数据无法自恢复 |
| 非法 | - | 文件级损坏 | 换文件/恢复备份 |
Chrome 场景下批量检测:
arduino
python sqlite_check.py ^
"%LOCALAPPDATA%\Google\Chrome\User Data\Default\Network\Cookies" ^
"%LOCALAPPDATA%\Google\Chrome\User Data\Default\History" ^
"%LOCALAPPDATA%\Google\Chrome\User Data\Default\Login Data" ^
"%LOCALAPPDATA%\Google\Chrome\User Data\Default\Web Data"
附录 B:卷影副本(VSS)提取与验证
§5 恢复决策树第一分支的完整操作。前提:系统保护已开启(很多机器默认只对系统盘开)。
makefile
:: 1. 列出快照(管理员)
vssadmin list shadows
:: 记录 Shadow Copy Volume: \?\GLOBALROOT\Device\HarddiskVolumeShadowCopy<N>
:: 注意 "Creation Time" ------ 只有早于损坏时间的快照才有意义
:: 2. cmd 提取会因路径转义失败(\?\GLOBALROOT 反斜杠被吃),必须 PowerShell:
php
# vss_extract.ps1(管理员运行)
$src = "\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Users<用户名>\AppData\Local\Google\Chrome\User Data\Default\Network\Cookies"
New-Item -ItemType Directory -Force -Path "D:\recover" | Out-Null
try {
Copy-Item -LiteralPath $src -Destination "D:\recover\Cookies_snapshot" -Force -EA Stop
"COPY_OK"
} catch { "COPY_FAIL: $($_.Exception.Message)" }
makefile
:: 3. ★必做:验证快照版本健康度(用附录 A 脚本)
python sqlite_check.py "D:\recover\Cookies_snapshot"
:: 表为空 = 快照时已损坏 → 此路不通,转 §5 决策树下一分支
实测教训:文件系统损坏是渐进的。本案例 10/5 中午的快照提取成功,但快照里的库同样是空表------白高兴一场。快照越早越好。
附录 C:Firefox Cookie 提取脚本(附去重逻辑)
python
# ff_extract.py --- Firefox 多容器会产生同名 Cookie 副本,必须去重
import sqlite3, json, shutil
# Firefox 运行中库被锁,先复制(连同 WAL,否则可能只读到旧数据)
PROF = r"C:\Users<用户名>\AppData\Roaming\Mozilla\Firefox\Profiles<profile>"
shutil.copyfile(PROF + r"\cookies.sqlite", r"D:\ff.db")
con = sqlite3.connect(r"D:\ff.db")
con.row_factory = sqlite3.Row
rows = con.execute("SELECT * FROM moz_cookies WHERE host LIKE '%目标域%'").fetchall()
seen = {} # (host, name, path) 三元组去重
for r in rows:
seen[(r['host'], r['name'], r['path'])] = dict(r)
out = [{
"domain": r['host'], "name": r['name'], "value": r['value'],
"path": r['path'],
"expires": r['expiry']*1000000 + 11644473600000000, # Unix秒 → Chrome微秒
"secure": bool(r['isSecure']), "httpOnly": bool(r['isHttpOnly']),
} for r in seen.values()]
json.dump(out, open("cookies.json", "w"), ensure_ascii=False)
print(f"导出 {len(out)} 条")
前置校验(别急着迁移,先确认凭证还有效):
python
import sqlite3, time
con = sqlite3.connect(r"D:\ff.db")
now = time.time()
for n, h, v, e in con.execute(
"SELECT name,host,value,expiry FROM moz_cookies "
"WHERE name IN ('SESSDATA','DedeUserID','bili_jct')"):
print(f"{h} {n}: len={len(v)} {'有效' if e > now else '★已过期'}")
附录 D:AppX 栈僵死自检三连
§3 的快速定性:
bash
# 1. 查询挂起(>60s 无响应即中招)
Measure-Command { Get-AppxPackage -AllUsers -Name "*Subsystem*" }
# 2. 预配包列表(DISM 独立路径,AppXSvc 僵死时依然可用)
dism /online /Get-ProvisionedAppxPackages | findstr /i "Subsystem"
# 3. AppX 服务状态
Get-Service AppXSvc, ClipSVC, StateRepository | Format-Table Name, Status
三个结果对应三种处置:
- ① 挂起 + ③ Running → AppX 栈假死,干净启动最稳
- ② 有输出 → 用 DISM 删预配包(§3 第 1 层)
- ② 也挂 → 只剩干净启动一条路(§3 第 2 层)
附录 E:Docker 引擎健康自检 30 秒清单
perl
# ① 三个依赖服务全 RUNNING?
Get-Service WslService, vmcompute, hns | Format-Table Name, Status
# ② 发行版列表秒回?(>30s = 回去查 §2 幽灵键)
wsl -l -v
# ③ 发行版能启动?(秒回 BOOT_OK 才算健康)
wsl -d docker-desktop sh -c "echo BOOT_OK"
# ④ 后端日志最新错误(GUI 只会说 unable to start)
Get-Content "$env:LOCALAPPDATA\Docker\log\host\com.docker.backend.exe.log" -Tail 100 |
Select-String "TimedOut|failed to start|0x8"
# ⑤ 引擎探活(有 Server 版本号 = 活着)
docker info --format "{{.ServerVersion}} {{.Driver}}"
附录 F:本次修复全程操作时序(可照抄)
markdown
第 1 天:定性
1. chkdsk C: /f(预约)→ 重启自检 → 确认 0 bad sectors
2. Get-PhysicalDisk → SMART Healthy → 确认硬件无损
第 2 天:重建
3. reg query HKCU...\Lxss /s → 删幽灵发行版键(§2)
4. wsl --unregister docker-desktop → 模板重导入
5. wsl -d docker-desktop echo BOOT_OK(209ms = 服务健康)
6. Docker Desktop → docker info 见 Server 段
7. daemon.json 加 registry-mirrors → docker pull hello-world 成功(§6)
插曲(同步进行):
A. Chrome 六库全空 → VSS 提取失败(快照已坏)→ Firefox Cookie 迁移(§5)
B. WSL 2.7→3.0.1 MSI 五连败 → DISM 删预配包 + 干净启动 → 装好(§3/§4)
附录 A--E 脚本均为实测原样,参数中的
<用户名>/<profile>/<GUID>按机器替换。