create_always不能解决独占问题,因其仅控制文件创建逻辑,真正决定独占的是dwsharemode参数必须设为0;设为file_share_delete会破坏独占性;句柄需正确关闭并防止继承。

为什么 CREATE_ALWAYS 不能解决独占问题
很多人以为用 CREATE_ALWAYS 就能“抢到”文件,其实它只控制创建逻辑(覆盖或新建),完全不干涉共享行为。真正决定能否独占打开的,是 CreateFile 的第四个参数 dwShareMode —— 它默认是 0,但 Windows 下 0 并不等于“独占”,而是“禁止任何共享”,这恰恰是你要的效果,可偏偏很多人误设成 FILE_SHARE_READ 或其它值,导致后续进程还能打开。
实操建议:
-
dwShareMode必须设为0(即不传任何FILE_SHARE_*标志),这是实现写入级独占的唯一可靠方式 - 如果同时需要读写,用
GENERIC_READ | GENERIC_WRITE作dwDesiredAccess,但dwShareMode仍必须为0 - 注意:即使设了
0,若文件已被其他句柄以非独占方式打开(比如记事本、Excel),CreateFile会直接失败并返回INVALID_HANDLE_VALUE,GetLastError()是ERROR_SHARING_VIOLATION
FILE_SHARE_DELETE 是个危险的例外
这个标志允许其他进程在你打开期间删掉该文件(只要没被映射或执行)。但它和“独占”天然冲突——如果你本意是防止别人动文件,却加了 FILE_SHARE_DELETE,那别人调 DeleteFile 就能成功,而你的句柄还能继续读写(直到关闭),这容易引发静默数据丢失或路径失效。
实操建议:
- 除非你明确设计为“临时占位+允许外部清理”,否则永远不要加
FILE_SHARE_DELETE - 尤其警惕日志轮转、配置热重载等场景:加了它,可能让监控脚本误删正在写的日志文件
- 测试时可用
Process Explorer查看目标文件是否真被当前进程独占(右键 → Find Handle or DLL → 输入文件名)
用 OPEN_EXISTING 还是 CREATE_ALWAYS?看意图别看习惯
这两个标志影响的是“文件不存在时的行为”,和共享无关,但选错会导致意外失败。比如你想独占写入一个配置文件,但用了 CREATE_ALWAYS,结果发现每次启动都清空旧内容;又或者用 OPEN_EXISTING 打开一个还不存在的缓存文件,直接报 ERROR_FILE_NOT_FOUND。
实操建议:
- 要确保文件存在且只操作已有内容 → 用
OPEN_EXISTING - 要强制覆盖并重新开始 → 用
CREATE_ALWAYS,但记得先检查返回值是否真拿到了新句柄(它可能因权限/路径问题失败) - 想“有则打开、无则创建但不覆盖” → 用
OPEN_ALWAYS,但注意它不会独占已有文件——如果文件已存在且被别人开着,你仍会撞上ERROR_SHARING_VIOLATION
别忘了 CloseHandle 和异常安全
Windows 下文件句柄是系统资源,不关就一直锁着。C++里最容易漏关的地方是异常路径:比如构造 std::ofstream 失败后跳出了作用域,但你手写的 CreateFile 句柄还在。更隐蔽的是 RAII 封装不当——有人写了个 FileGuard,但析构函数里没判 INVALID_HANDLE_VALUE,结果对无效句柄调 CloseHandle 触发未定义行为。
实操建议:
- 所有
CreateFile成功返回的句柄,必须配对CloseHandle,且仅调一次 - RAII 类中,构造函数拿到句柄后立刻存为成员变量,析构前先判是否为
INVALID_HANDLE_VALUE - 调试时可在
CloseHandle后加GetLastError()检查(虽然它通常不改错误码,但能帮你确认没误关别的句柄)
独占的核心从来不在怎么开,而在谁持有、何时放、有没有被意外继承。子进程默认会继承句柄,如果父进程开了独占文件又没设 bInheritHandle = FALSE,子进程一启动就可能把锁带过去,你自己都关不掉。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











