GetFileInformationByHandle 无法直接判断谁锁定了文件,仅能通过 CreateFile 失败时的 ERROR_SHARING_VIOLATION 确认存在共享冲突;真正定位占用进程需用 Restart Manager API(RmStartSession→RmRegisterResources→RmGetList),但须确保路径绝对、大小写精确、避开重定向与临时文件干扰。

怎么用 GetFileInformationByHandle 判断文件是否被锁定
Windows 下没有直接返回“谁锁了这个文件”的 API,GetFileInformationByHandle 本身不提供进程信息,但它能帮你确认「当前句柄是否独占打开」——这是判断锁定的前提。如果调用它成功,说明你拿到了有效句柄;如果失败且 GetLastError() 返回 ERROR_SHARING_VIOLATION,基本可断定文件正被其他进程以不兼容的共享模式打开(比如别人用 FILE_SHARE_NONE 打开了)。
常见错误现象:CreateFile 失败但只查 ERROR_ACCESS_DENIED,漏掉 ERROR_SHARING_VIOLATION ——后者才是文件被占用的典型信号。
- 必须用
GENERIC_READ或GENERIC_WRITE+ 不带FILE_SHARE_WRITE/FILE_SHARE_READ的方式尝试打开,否则即使被占也会成功(共享模式掩盖了冲突) - 不要依赖
GetLastError()的值在任意 API 调用后直接读取,必须在目标 API(如CreateFile)返回INVALID_HANDLE_VALUE后立刻调用 -
GetFileInformationByHandle不能告诉你哪个进程在用,它只是帮你验证:你自己的打开是否因共享冲突而失败
如何用 RestartManager API 查出占用进程 PID
真正能定位到进程的方案是 Windows Restart Manager(从 Vista 起内置),它专为“找出谁在用某文件”设计,比暴力枚举句柄靠谱得多,且无需管理员权限(部分场景除外)。
使用场景:你要删除/替换一个正在被 Excel、Notepad++ 或某个服务占用的 DLL/LOG 文件,需要提前通知用户或强制释放。
- 调用顺序固定:
RmStartSession→RmRegisterResources(传入文件路径数组)→RmGetList(关键!返回占用该文件的RM_UNIQUE_PROCESS数组,含 PID 和进程名) -
RmGetList返回的进程名可能为空(比如 svchost 托管的服务),此时得靠 PID 配合OpenProcess+QueryFullProcessImageName补全路径 - 注意
RmRegisterResources接收的是LPCWSTR,路径必须是绝对路径,相对路径或短文件名(8.3)会导致注册失败,RmGetList返回空结果 - 某些反病毒软件或受保护进程(如 CSRSS)可能拒绝被 Restart Manager 枚举,这不是 bug,是系统限制
为什么不用 NtQuerySystemInformation 枚举句柄
网上很多教程教用 NtQuerySystemInformation + SystemHandleInformation 遍历全系统句柄,再用 NtQueryObject 检查每个句柄是否指向目标文件。理论上可行,但实际极难落地:
- 必须以管理员权限运行,普通用户调用直接
STATUS_ACCESS_DENIED - 返回的句柄信息里没有文件路径,只有对象名(如
DeviceHarddiskVolume1oo.txt),需手动解析设备映射,遇到卷挂载点、符号链接、重定向(如 WSL2)极易匹配失败 - 性能极差:一次遍历动辄上万句柄,
NtQueryObject是内核态调用,频繁触发上下文切换,卡顿明显 - Windows 10 1809+ 对非微软签名驱动/进程隐藏部分句柄,导致漏报,且该 API 无正式文档支持,随时可能被废弃
实战中容易忽略的路径与权限细节
哪怕用了 Restart Manager,照样会失败——问题往往不出在逻辑,而在路径和权限的毛刺上。
- 传给
RmRegisterResources的路径必须和实际被占用的路径完全一致:比如进程打开的是C:Tempile.log,你传C: empFILE.LOG(大小写/斜杠方向不同)或\?C:Tempile.log(NT 对象路径格式)都会匹配不上 - 如果文件在 OneDrive 或 Dropbox 同步目录下,可能被其进程以“重定向”方式打开,Restart Manager 可能只报出云客户端 PID,而非最终使用者(如 Word)
- 调用前先用
GetFinalPathNameByHandle获取目标文件的真实路径(绕过符号链接),再传给 Restart Manager,能显著提升命中率 - 某些程序(如 Visual Studio)会先创建临时文件再原子替换,你以为锁的是
config.json,其实当时被占的是config.json.tmp——得结合日志或 Process Monitor 确认真实操作对象
真正卡住人的,从来不是 API 调用几行代码,而是路径大小写、符号链接展开、同步工具劫持、临时文件命名这些藏在表层之下的细节。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











