Windows无直接API查文件锁定进程,工具需枚举句柄反查路径但易失败;RestartManager API可绕过此限制获取PID列表,但需管理员权限、绝对路径且不支持网络路径,失败时可能因驱动锁定或删除待定状态。

为什么 GetFileInformationByHandle 和常规句柄枚举无法直接告诉你“谁锁了这个文件”
Windows 本身不提供“给定文件路径 → 返回占用进程 ID”的原子 API。你看到的很多工具(如 Process Explorer、Handle.exe)其实是在做两件事:先枚举所有进程的句柄,再对每个句柄调用 GetFinalPathNameByHandle 或 DeviceIoControl(..., FSCTL_GET_REPARSE_POINT, ...) 反查路径——但这条路在锁定场景下极易失败。因为被独占打开的文件,其句柄可能不暴露完整路径(尤其当对方用 FILE_FLAG_DELETE_ON_CLOSE 或通过卷设备路径打开时),甚至根本无法通过 NtQueryObject 获取名称。
用 RestartManager API 绕过句柄解析,直接获取锁定进程列表
这是 Windows Vista+ 提供的、专为安装/更新场景设计的合法绕行方案。它不依赖句柄路径解析,而是由系统内核维护一份“哪些进程打开了哪些资源”的映射快照。关键函数是 RmStartSession → RmRegisterResources → RmGetList。
-
RmRegisterResources接收的是文件路径数组(LPCTSTR),不是句柄,因此能处理符号链接、重解析点、甚至已删除但仍被打开的文件 - 必须以管理员权限运行,否则
RmGetList返回ERROR_ACCESS_DENIED - 返回的
RM_PROCESS_INFO结构里含dwProcessId和strAppName,但后者不可靠(可能为空或为“Unknown”),应以 PID 为准 - 注意:该 API 对网络路径(
\servershare)支持有限,仅适用于本地卷上的文件
实际调用时容易忽略的三个坑
即使调用流程写对,仍常因以下原因失败:
- 未调用
SetErrorMode(SEM_FAILCRITICALERRORS),导致某些进程弹出“找不到 DLL”对话框阻塞枚举 - 传入的文件路径必须是**绝对路径**且经
GetFullPathName规范化,不能带.或..;若目标是目录,需确保末尾有反斜杠(C:dir),否则可能匹配失败 -
RmGetList返回的进程列表中,部分 PID 可能已退出(STATUS_PROCESS_IS_TERMINATING),需用OpenProcess+WaitForSingleObject粗略验证存活,但不要TerminateProcess—— 这属于越权操作
如果连 RestartManager 都返回空列表,说明什么
常见于以下情况:
立即学习“C++免费学习笔记(深入)”;
- 文件被内核驱动(如防病毒软件、备份工具)直接通过
IoCreateFile打开,绕过了用户态资源管理器注册 - 文件处于“删除待定”状态(
FILE_ATTRIBUTE_TEMPORARY+FILE_FLAG_DELETE_ON_CLOSE),此时RmRegisterResources可能静默忽略 - 你正在查询的是重解析点(junction/symlink)的目标路径,而非原始路径本身——得先用
GetFinalPathNameByHandle反向解析出真实路径再试
这种情况下,唯一较稳妥的补救手段是调用 NtQuerySystemInformation(SystemProcessInformation) 手动遍历所有进程的句柄,再逐个用 NtQueryObject 尝试读取 ObjectNameInformation,但成功率取决于目标进程的访问令牌和 SE_DEBUG_PRIVILEGE 权限是否启用。



















