解锁、并强制删除 Windows 不肯放手的文件。
先告诉你是哪个进程占着,再动手清掉 —— 窗口、命令行、右键菜单,三种方式都行。
浅色 / 深色 —— 这是一次真实扫描:explorer.exe 和 TSVNCache.exe 占着 D:\archive\cc1 里的 7 个文件
你想删一个文件夹,Windows 只回你一句:
操作无法完成,因为文件夹或其中的文件已在另一个程序中打开 拒绝访问 另一个程序正在使用此文件,进程无法访问。
大多数工具的做法是甩给你一个「解锁」按钮。点下去,失败了;重启之后能删了 —— 但你始终不知道刚才到底发生了什么。
UnlockKit 先给诊断,再动手。它枚举全系统打开的文件句柄,按卷序列号 + 文件 ID 匹配你的目标(不是比字符串,所以硬链接、\\?\ 别名、盘符别名都骗不过它),明确告诉你五种可能原因里,你中的是哪一个。只有这一步之后它才行动 —— 用能生效的最轻手段,而不是最猛的那个。
- 指名道姓。 进程名、PID,以及它到底开着了哪些句柄、拿到了什么访问权限。不会再有「某个程序」。
- 扫描文件夹内部。 目标是目录时,它遍历整棵子树。因为文件夹本身几乎从来不是被占用的那个对象 —— 被占的是里面的某个子文件,而监视器(SVN/Git 图标缓存、杀毒、同步盘、资源管理器缩略图)会在毫秒级重新打开它。
- 用 POSIX 语义删除,即使还有别的句柄开着,文件名也能立刻解除链接、字节在最后一个句柄关闭时释放。只读属性一并忽略,因为这在同一次调用里就能指定。
- 关闭阻塞的句柄 —— 光靠 POSIX 不够时才做。每关一个之前都会重新校验它指向的还是不是那个对象,所以被回收复用的句柄号绝不可能误伤别的东西。
- 结束进程作为最后手段,并且做不到时如实说明(
PPL、僵尸进程、权限不足)。 - 安排重启时删除 —— 当这个锁从用户态确实破不了,就通过
PendingFileRenameOperations交给下次开机,在SMSS阶段执行。 - 右键集成 —— 一个
UnlockKit父项,下挂Unlock和Delete,各带图标;只写当前用户,全程不需要管理员权限。 - 深色模式 —— 首次启动跟随系统(任务栏里的浅色/深色),右下角一键切换,右键那个开关可以选「跟随 Windows」;选择存在
HKCU\Software\UnlockKit,标题栏也一起变深,不会出现白色边框配黑窗口。 - 自动更新 —— 每次启动在后台查一次、有新版本自动下载、每天最多提醒一次;状态栏那个药丸随时可以手动检查,也可以
--update全自动无窗口跑。
从 Releases 下载 UnlockKit.exe —— 单个自包含文件,不需要装 .NET 运行时,约 68 MB。
然后二选一:
- 直接运行。 它是绿色版,可以把文件或文件夹拖进窗口,也可以直接当参数传进去。
- 点窗口里的
Install context menu按钮。 它会把可执行文件复制到%LOCALAPPDATA%\Programs\UnlockKit\UnlockKit.exe,并写入当前用户的HKCU菜单项。再点一次(此时按钮显示Remove context menu)就能撤掉。
Windows 11 注意: 这是传统注册表菜单项,所以会出现在**「显示更多选项」**里(或者按
Shift+F10)。要进一级菜单,必须用 MSIX 打包并带应用标识的IExplorerCommand—— 这是 shell 的策略,不是注册表能绕过去的,为一个工具去背打包的副作用也不值。
未签名: 没有代码签名证书,所以第一次运行 SmartScreen 会提示「Windows 已保护你的电脑」。点「更多信息 → 仍要运行」,或者干脆用页面最下方那两条命令自己从源码构建。
界面目前只有英文。 功能、菜单、日志都是英文;这份说明是中文的。
程序会自己更新,不用每次去 Releases 页面下载。
- 启动时在后台检查一次 —— 窗口出现约 2 秒后发起,不挡任何操作。有新版本就自动下载(约 68 MB,走
api.github.com,不经过任何中转服务器)。 - 每天最多提醒一次。下载完成后,窗口里会出现一条提示:「UnlockKit x.y.z is ready」+
Restart and update/Later/Skip。点Later今天就不再打扰;点Skip这个版本以后都不再提示(更晚的版本仍会提示)。 - 随时可以手动更新。右下角状态栏的
Check updates就是入口:平时是检查,下载中显示百分比,就绪时变成Update x.y.z,点一下直接装。 - 点
Restart and update之后,窗口关闭、替换 exe、再以新版本启动,大概两秒。
装到哪儿:新版本会落在当前 exe 所在的目录。如果那儿不可写(比如你把它放在 Program Files),它会退回到 %LOCALAPPDATA%\Programs\UnlockKit\,不会弹权限错误。
下载的东西会校验三道:SHA256(发布页同时挂 .sha256 文件)、MZ 头、以及文件里编译进去的版本号。任何一项对不上就丢弃、不执行。
不想让它联网:设环境变量 UNLOCKKIT_NO_UPDATE=1,启动时的自动检查就跳过(手动检查仍然可用)。脚本里想强制更新,用:
UnlockKit.exe --update # 检查 + 下载 + 替换 + 重启,不显示窗口;退出码 0 成功,1 失败日志在 %LOCALAPPDATA%\UnlockKit\update.log,更新失败时先看它。
1. 把删不掉的文件夹拖进窗口
2. Scan -> "LOCKED (subtree): 5 file(s) inside are held by 1 process"
TSVNCache.exe pid 8124
0x1234 READ|WRITE|DELETE
-> D:\archive\ccccc\assets\config.json
3. Unlock -> 关掉这些句柄,文件夹本身不动
4. Delete -> 没了
引擎同时是一个独立控制台程序 —— unlockkit-cli.exe,同一个发布页。适合写进脚本、构建步骤和 CI。
unlockkit-cli "D:\archive\ccccc" # 只诊断(默认)
unlockkit-cli diagnose "D:\archive\ccccc" # 同上,写明动作
unlockkit-cli delete "D:\archive\ccccc" # 立即删除(POSIX 语义)
unlockkit-cli delete "D:\archive\ccccc" --reboot # 允许排到下次开机删除
unlockkit-cli close "D:\archive\ccccc" # 只关句柄,保留文件
unlockkit-cli kill "D:\archive\ccccc" # 结束占用的进程| 参数 | 含义 |
|---|---|
--json |
机器可读输出,每次运行一个对象 |
-v, --verbose |
打印每个句柄的完整对象路径 |
--reboot |
允许使用 PendingFileRenameOperations 兜底 |
-h, --help |
显示用法 |
退出码是稳定的,可以直接 if errorlevel 判断:
| 退出码 | 含义 |
|---|---|
0 |
成功,或者本来就没被占用 |
1 |
被占用 —— 动作被拒绝或失败 |
2 |
参数错误,或路径无法读取 |
诊断是一个结论,不是一个布尔值。结论决定接下来做什么。
| 结论(实际输出) | 含义 | UnlockKit 的处理 |
|---|---|---|
NOT LOCKED |
没有用户态句柄占着,且 DELETE 权限可用 |
正常删除 |
LOCKED but deletable |
有句柄,但对方开了 FILE_SHARE_DELETE |
一次 POSIX 删除即可,不解任何句柄 |
LOCKED |
句柄没开 share-delete | 关掉这些句柄,再 POSIX 删除 |
LOCKED (subtree) |
文件夹本身空闲,但里面的文件被占 | 一次性关掉整棵子树,自底向上删 |
ACL BLOCKS DELETE |
没人占用;这个账户缺 DELETE 权限 |
拒绝,并告诉你这是权限问题,不是锁 |
KERNEL-HELD LOCK |
ERROR_SHARING_VIOLATION,但没有任何可关的句柄 |
拒绝(除非你主动要求排到开机时删) |
最后一行是关键。当文件被过滤驱动占住,没有任何用户态工具能破 —— 不是这个,也不是别的。UnlockKit 会直接说清楚,而不是假装失败:一个锁只有一个原因,含糊其辞正是那些「解锁器」口碑崩掉的原因。
现实里删不掉的情况,大约一半能从用户态解决。剩下一半是结构性的,点多少次都没用:
- 内核驱动持有 —— 杀毒软件的 minifilter、备份代理、云同步客户端在过滤驱动里持有文件。用户态关不掉那些句柄,重启删除是唯一出口。
- 映像节被映射 —— 正在运行的
.exe、已加载的.dll删不掉。返回STATUS_CANNOT_DELETE,而且这个行为是对的。 - PPL 保护进程 ——
MsMpEng、services、lsass这类进程受保护,即使有SeDebugPrivilege也打不开。UnlockKit 会按名单拒绝,并告诉你它的保护级别。 - 正在终止的进程 —— 卡在终止流程里的进程,句柄要等内核回收才释放。等,或者重启。
- ACL 问题 —— 缺
DELETE权限是权限问题。UnlockKit 能准确诊断,但不会背着你偷偷夺取文件所有权。
拒绝碰 System、csrss、wininit、winlogon、services、lsass 和杀毒引擎,是故意的,不是待修复的限制。在那些进程里关句柄,轻则桌面无响应,重则直接蓝屏。
要求:Windows 10/11 x64、.NET 9 SDK。
git clone https://github.com/MikeKen-Ken/UnlockKit.git
cd UnlockKit
dotnet build -c Release
# 单文件 GUI
dotnet publish src/UnlockKit.UI/UnlockKit.UI.csproj -c Release -r win-x64 ^
--self-contained true -p:PublishSingleFile=true ^
-p:IncludeNativeLibrariesForSelfExtract=true -p:EnableCompressionInSingleFile=true -o dist/gui
# 单文件 CLI
dotnet publish src/UnlockKit/UnlockKit.csproj -c Release -r win-x64 ^
--self-contained true -p:PublishSingleFile=true ^
-p:IncludeNativeLibrariesForSelfExtract=true -p:EnableCompressionInSingleFile=true -o dist/cliCI 每次 push 都跑一遍同样的流程,推 v* tag 时会把两个二进制挂到 GitHub Release 上 —— 见 .github/workflows/build.yml。
src/
UnlockKit/ 引擎 + CLI —— 句柄扫描、安全策略、删除阶梯
Native/ P/Invoke 层(ntdll、kernel32、advapi32)
Core/ 扫描器、进程探测、路径映射、安全策略
Actions/ POSIX 删除、句柄关闭、递归树删除
UnlockKit.UI/ WPF 前端、用户级 shell 集成、自安装
Themes/ 浅色 / 深色两套颜色字典 + 全部控件样式
docs/
DESIGN.md 每个机制为什么存在,代价是什么
DESIGN.en.md 同上(英文)
TROUBLESHOOTING.md 故障分类手册 —— 提 issue 前先看这个
TROUBLESHOOTING.en.md 同上(英文)
assets/ 图标与截图源文件
文档和 README 一样是双语的:
- docs/DESIGN.md —— 句柄扫描、身份匹配、删除阶梯,以及 TOCTOU 上的取舍。
- docs/TROUBLESHOOTING.md —— 「我还是删不掉」。按你看到的报错组织。
英文版见 DESIGN.en.md / TROUBLESHOOTING.en.md。
MIT。随便用,没有担保 —— 你要是指错了路径,它会很乐意把错的东西删掉。