最直接有效的方式是调用CreateFile以dwShareMode=0和OPEN_EXISTING尝试独占打开,若返回INVALID_HANDLE_VALUE且GetLastError()为ERROR_SHARING_VIOLATION,则文件被其他进程以不兼容共享模式独占占用。

Windows下用CreateFile探测文件锁定状态
最直接有效的方式是尝试以独占方式打开文件,靠系统返回的错误码判断是否被占用。Windows不提供“查询锁定状态”的API,只能试探性打开。
关键点在于:必须使用CREATE_ALWAYS或OPEN_EXISTING + FILE_SHARE_NONE,且不能带FILE_ATTRIBUTE_NORMAL以外的冗余标志。常见错误是误加GENERIC_WRITE——即使只读检测,也应只用GENERIC_READ,否则可能因权限不足误报锁定。
-
dwDesiredAccess设为GENERIC_READ(或0,仅需句柄存在性) -
dwShareMode必须为0(即FILE_SHARE_NONE) -
dwCreationDisposition推荐OPEN_EXISTING,避免意外创建文件 - 若返回
INVALID_HANDLE_VALUE,再调GetLastError();值为ERROR_SHARING_VIOLATION即确认被锁定
Linux/macOS用open配合O_EXCL和O_NONBLOCK
Unix系没有“文件锁定”概念,只有进程对文件描述符的持有和flock/fcntl锁。但实际中,被其他进程以O_RDWR或O_WRONLY打开且未设共享标志时,open(..., O_RDONLY | O_EXCL)会失败——这可作为粗略判断依据。
更可靠的做法是结合flock()尝试加锁:flock(fd, LOCK_SH | LOCK_NB)失败且errno == EWOULDBLOCK,说明已有排他锁存在。注意:这只能检测flock锁,对mmap、普通open无锁行为无效。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 单纯
open(path, O_RDONLY | O_NONBLOCK)总能成功,无法判断占用 -
O_EXCL只有在搭配O_CREAT时才有意义,单独使用会被忽略 - 真正可用的是
flock()或fcntl(fd, F_SETLK, &fl),后者能检测POSIX记录锁 - 若目标文件是设备节点或NFS挂载点,
flock可能不可靠,需fallback到stat + 进程扫描
跨平台封装时为什么不能依赖std::filesystem::exists
std::filesystem::exists只检查路径是否存在且可访问,完全不反映当前打开状态。即使文件正被记事本编辑,它仍返回true;反之,若权限不足导致无法读取元数据,它也可能返回false——这不是锁定,是权限问题。
更隐蔽的坑是:某些杀毒软件或备份工具会在后台静默打开文件(如实时扫描),此时用户代码用CreateFile失败,但exists一切正常,容易误判为“文件没问题”。这类场景必须走底层API试探,而非依赖C++17标准库的抽象层。
-
exists等价于Windows的GetFileAttributes,不触发打开逻辑 - 试图用
std::fstream构造函数捕获异常来判断?不行——构造成功不代表独占,也不代表后续读写不失败 - Boost.Filesystem的
status()同样不解决锁定问题,只是包装了相同系统调用
容易被忽略的边界情况:删除中、重命名中、网络文件系统
文件正在被另一个进程删除(如DeleteFile未完成)时,CreateFile可能返回ERROR_ACCESS_DENIED而非ERROR_SHARING_VIOLATION;重命名过程中(MoveFileEx进行中)也可能出现ERROR_USER_MAPPED_FILE。这些都不是“被锁定”,但行为类似。
网络文件系统(SMB/NFS)上,锁语义更弱:flock在NFSv3下默认不生效,Windows SMB共享中FILE_SHARE_DELETE常被忽略,导致本地测试通过、部署后失败。
- 不要只检查单一错误码,需覆盖
ERROR_SHARING_VIOLATION、ERROR_ACCESS_DENIED、ERROR_USER_MAPPED_FILE - 对UNC路径(
\servershareile.txt),CreateFile可能额外返回ERROR_BAD_NETPATH,需先确认网络可达 - 容器环境里,宿主机进程可能持有文件句柄,但容器内
lsof看不到——此时只能靠重试+超时,无法精确判断

















