Total Commander 主线程卡死问题排查

Total Commander 主线程卡死问题排查——百度网盘 Shell 扩展:你搁这等啥呢?

  • Total Commander 主线程卡死问题排查
    • 问题现象
    • 排查工具
    • 排查步骤
      • 第一步:给卡死的进程拍个"遗照"(生成 Dump)
      • 第二步:请出 WinDbg 大夫,加载 Dump
      • 第三步:看看这是啥类型的 Dump
      • 第四步:翻主线程的调用栈(破案关键)
      • 第五步:确认"凶手"身份
    • 根因分析
      • 调用链还原
      • 来龙去脉
    • 解决方案
      • 方案一:打开百度网盘设置,禁用 Shell 扩展(推荐)
      • 方案二:卸载百度网盘

Total Commander 主线程卡死问题排查

问题现象

某天正愉快地用 Total Commander(64位)管理文件,右键一点……好家伙,整个界面直接石化,鼠标转圈圈,窗口标题栏写着大大的"未响应"。只能含泪打开任务管理器把它送走。

用 PowerShell 确认一下,果然凉了:

Get-Process-Name"TOTALCMD64"|Select-ObjectId,ProcessName,Responding

输出:

Id ProcessName Responding -- ----------- ---------- 1234 TOTALCMD64 False

Responding = False,实锤了——主线程消息循环彻底罢工。


排查工具

工具干啥用的
PowerShell确认进程状态、生成 dump 文件
WinDbg打开 dump 文件,翻线程调用栈找"凶手"
dbghelp.dll提供MiniDumpWriteDumpAPI,用来给进程"拍快照"

排查步骤

第一步:给卡死的进程拍个"遗照"(生成 Dump)

进程卡死了但没崩,系统不会自动生成 dump,得我们自己动手。

写个 PowerShell 脚本dump.ps1

Add-Type-TypeDefinition @" using System; using System.Runtime.InteropServices; public class MiniDumpHelper { [DllImport("dbghelp.dll", SetLastError = true)] public static extern bool MiniDumpWriteDump( IntPtr hProcess, uint processId, IntPtr hFile, uint dumpType, IntPtr exceptionParam, IntPtr userStreamParam, IntPtr callbackParam); } "@$processId= <目标进程PID>$process=Get-Process-Id$processId$dumpPath="D:\temp\totalcmd64.dmp"$fileStream=New-ObjectSystem.IO.FileStream($dumpPath,[System.IO.FileMode]::Create)# dumpType=2 表示 MiniDumpWithFullMemory(完整内存快照)$result=[MiniDumpHelper]::MiniDumpWriteDump($process.Handle,$processId,$fileStream.SafeFileHandle.DangerousGetHandle(),2,[IntPtr]::Zero,[IntPtr]::Zero,[IntPtr]::Zero)$fileStream.Close()Write-Host"Dump created:$result"

跑起来:

powershell-ExecutionPolicy Bypass-File dump.ps1

看到Dump created: True,快照到手。


第二步:请出 WinDbg 大夫,加载 Dump

.sympath srv*C:\Symbols*https://msdl.microsoft.com/download/symbols .open D:\temp\totalcmd64.dmp .reload

符号服务器配好,微软的 DLL 就都能解析出函数名了。


第三步:看看这是啥类型的 Dump

.exr -1

输出:

ExceptionAddress: 0000000000000000 ExceptionCode: 80000003 (Break instruction exception) ExceptionFlags: 00000000 NumberParameters: 0

80000003是断点异常——说明这是我们手动抓的快照,不是崩溃。分析方向:挂起/死锁


第四步:翻主线程的调用栈(破案关键)

切到主线程,看看它到底卡在哪儿:

~0s k

调用栈(精简版,从上到下是从"表象"到"根源"):

ntdll!NtWaitForSingleObject+0x14 ← 卡在这儿:死等一个内核对象 KERNELBASE!WaitForSingleObjectEx+0x8e SHCore!SHCreateThread+0x143 windows_storage!DataAccessCaches_InvalidateForLibrary+0x274a ... shell32!SHELL32_IconCache_AboutToExtractIcons+0x1df user32!DispatchMessageW+0x741 user32!CreateWindowExW+0x82 YunShellExtV164+0xd8232 ← ★ 就是你!百度网盘Shell扩展 YunShellExtV164+0x1106c8 YunShellExtV164!DllGetClassObject+0x69 ← ★ 初始化COM对象时出的事 YunShellExtV164!DllUnregisterServer+0x57bc ntdll!LdrLoadDll+0xfa KERNELBASE!LoadLibraryExW+0x172 combase!CoCreateInstance+0x14c ← 系统在创建COM实例 shell32!SHELL32_SHCreateDefaultContextMenu ← 系统在构建右键菜单 TOTALCMD64+0x20013e ← TC说:我就想弹个右键菜单啊!

一眼看出来了:百度网盘的 Shell 扩展在初始化时卡住了,连累整个主线程一起躺平。


第五步:确认"凶手"身份

lm m YunShellExtV164

输出:

start end module name 00007ffc`5db60000 00007ffc`5ddf7000 YunShellExtV164 (export symbols) YunShellExtV164.dll

YunShellExtV164=“云”(Yun) + ShellExt + V1 + 64位→ 百度网盘的右键菜单扩展,破案了。


根因分析

调用链还原

TC: 我想弹个右键菜单

shell32: 好嘞,让我加载所有扩展

COM: 来,加载百度网盘扩展

百度网盘: 等我初始化一下...

百度网盘: 我要创建个窗口

windows_storage: 帮我枚举一下存储信息

SHCore: 开个线程干活,你等着

主线程: 等...等...等到天荒地老 ☠️

来龙去脉

  1. Total Commander 在主线程上同步调用SHCreateDefaultContextMenu想弹个右键菜单
  2. 系统乖乖地逐个加载所有注册的右键扩展,轮到了百度网盘的YunShellExtV164.dll
  3. 这哥们在初始化时搞了一堆骚操作:创建窗口、枚举存储信息……
  4. 最后开了个工作线程,然后主线程傻等这个线程干完活
  5. 然而那个线程似乎永远也干不完 → 主线程:???我等到花儿都谢了

解决方案

方案一:打开百度网盘设置,禁用 Shell 扩展(推荐)

第一步:打开百度网盘 → 点击右上角菜单按钮 → 选择「设置」

第二步:取消勾选"在我的电脑显示网盘"

第三步:在任务管理器中杀掉 Total Commander 进程,重新启动即可恢复正常。

方案二:卸载百度网盘

简单粗暴,一了百了。如果你平时不怎么用百度网盘桌面端,直接卸了清净。