最直接有效的方式是尝试以generic_read权限和dwsharemode=0调用createfile,若返回invalid_handle_value且getlasterror()为error_sharing_violation或error_access_denied,则文件被独占占用;成功则需立即closehandle避免句柄泄漏。

Windows 下用 CreateFile 检测文件是否被独占
最直接有效的方式是尝试以独占模式打开文件,看系统是否拒绝。Windows 的 CreateFile 在文件已被其他进程以不兼容共享模式打开时,会返回 INVALID_HANDLE_VALUE,且 GetLastError() 为 ERROR_SHARING_VIOLATION 或 ERROR_ACCESS_DENIED。
关键不是“读取内容”,而是“能否获得句柄”——哪怕只读、只共享读,只要对方打开了 FILE_SHARE_NONE,你就进不去。
- 必须指定
dwShareMode = 0(即不共享),否则即使文件被占用也可能成功返回句柄 - 使用
GENERIC_READ足够,不需要写权限;避免触发 UAC 或权限拒绝干扰判断 - 务必调用
CloseHandle(),否则可能泄漏句柄(尤其在循环检测时) - 示例片段:
HANDLE h = CreateFile(L"test.txt", GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); if (h == INVALID_HANDLE_VALUE) { DWORD err = GetLastError(); if (err == ERROR_SHARING_VIOLATION || err == ERROR_ACCESS_DENIED) { // 文件被独占 } } if (h != INVALID_HANDLE_VALUE) CloseHandle(h);
Linux/macOS 下用 open + O_EXCL 不适用,得换思路
O_EXCL 只在 O_CREAT 时有意义,对已存在文件无效,所以不能靠它判断占用。实际可行的是尝试以只读+非阻塞方式打开,并检查是否因资源忙被拒——但 Linux 本身不阻止多进程同时读同一文件,所谓“独占”通常指写入锁或 flock 占用。
- 若目标进程用了
flock(fd, LOCK_EX),你可以用flock(fd, LOCK_SH | LOCK_NB)尝试加共享锁:失败且errno == EWOULDBLOCK表示有排他锁存在 - 注意:
flock是建议性锁,不强制;如果对方根本没加锁,你永远检测不到“占用” - 没有通用方法检测“被 mmap 写入中”或“被 std::ofstream 未关闭句柄占用”,这类属于进程内部状态,内核不暴露
- 简单探测可结合
lsof -p $(pidof your_target_proc) | grep filename,但生产环境不应依赖 shell 命令
跨平台封装要注意的三个坑
别写“一次编写,到处判断”——Windows 和 Unix 对“文件占用”的定义根本不一致。强行抽象反而增加误判。
- Windows 的
CreateFile检测的是内核级打开句柄冲突;Unix 的flock检测的是用户态协作锁,二者语义不同,不能混为一谈 - 不要用
std::filesystem::status()或access(),它们只查权限和存在性,完全无法反映是否被占用 - 某些杀毒软件或编辑器(如 VS Code)会在后台持有文件句柄但不加
flock,这种 Windows 下能被CreateFile捕获,Linux 下基本无解
为什么 ifstream::is_open() 或 fopen 返回 true 不代表没被占
因为 C++ 标准库的文件流打开失败时,往往只告诉你“打不开”,却不区分失败原因是路径不存在、权限不足,还是被独占——错误码被封装掉了。
-
std::ifstream f("x.txt"); if (!f.is_open())→ 你不知道是ENOENT还是EACCES还是EBUSY -
fopen("x.txt", "r") == nullptr同样丢失细节;必须用底层系统调用才能拿到原始 errno / GetLastError() - 想做可靠判断,就得绕过标准库 IO 接口,直连系统 API;封装一层薄薄的
try_open_exclusive()比依赖流状态更可控
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











