createfile返回invalid_handle_value却无显式错误,通常因error_sharing_violation——文件被其他进程以不兼容共享模式打开;必须设dwsharemode=0、dwcreationdisposition为open_existing或create_always、htemplatefile为null,且确保无残留句柄。

为什么 CreateFile 返回 INVALID_HANDLE_VALUE 却没报错?
常见现象是调用后直接失败,GetLastError() 返回 ERROR_SHARING_VIOLATION——这说明文件正被其他进程(甚至同一进程的另一处代码)以不兼容的共享模式打开。
独占式打开的关键不是“加锁”,而是从打开那一刻起就拒绝所有其他访问。必须显式设置 dwShareMode = 0,即禁止任何共享。
-
dwShareMode = 0是硬性要求,哪怕只是想读取也不能设FILE_SHARE_READ - 如果文件已由
fopen、std::fstream或其他CreateFile实例打开过,当前调用必败 - 注意:Explorer.exe 有时会因缩略图/属性面板临时打开文件,导致看似“空闲”的文件实际被占用
LockFileEx 和 CreateFile 的独占逻辑区别在哪?
CreateFile 的独占是“打开级”的,靠系统内核在句柄创建时拦截;LockFileEx 是“内容级”的,作用于已打开句柄的某段字节范围,且不阻止别人重新 CreateFile —— 它俩解决的是不同层次的问题。
- 只想要“整个文件不让别人碰”,就别调
LockFileEx,老老实实设dwShareMode = 0并确保无残留句柄 -
LockFileEx需要先有有效句柄,且锁定区域可能被其他进程绕过(比如对方也用CreateFile(..., 0, ...)强行打开) - 若需跨进程协作式互斥,
CreateMutex比文件锁更可靠;文件锁更适合防止意外覆盖
实际写法中容易漏掉的三个参数细节
光设 dwShareMode = 0 不够,这三个参数配合错误,照样拿不到独占句柄。
-
dwCreationDisposition必须是OPEN_EXISTING(只打开已有)或CREATE_ALWAYS(覆盖重开),不能用OPEN_ALWAYS—— 后者在文件存在时仍会尝试共享打开,破坏独占语义 -
dwFlagsAndAttributes中若含FILE_ATTRIBUTE_HIDDEN或FILE_ATTRIBUTE_SYSTEM,某些杀软或UAC策略会静默拒绝,建议仅用FILE_ATTRIBUTE_NORMAL -
hTemplateFile必须传NULL,传任意值都会导致INVALID_HANDLE_VALUE且GetLastError返回ERROR_INVALID_PARAMETER
示例片段:
HANDLE h = CreateFile(
L"config.dat",
GENERIC_READ | GENERIC_WRITE,
0, // ← 关键:禁止共享
NULL,
OPEN_EXISTING, // ← 关键:不创建新文件,也不允许共享打开已有文件
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (h == INVALID_HANDLE_VALUE) {
DWORD err = GetLastError(); // 查具体原因
}
关闭句柄后文件真的“释放”了吗?
是的,但有个隐蔽前提:你得真正关掉了所有副本句柄。Windows 的句柄是引用计数的,只要还有一个线程持有该句柄(哪怕是复制过的),文件就仍处于被独占状态。
- 用
CloseHandle关闭后,立即用另一个进程尝试CreateFile(..., 0, ...)测试是否释放成功 - 调试时可用
Process Explorer搜索文件名,确认没有残留句柄(包括你自己的其他线程) - 避免在异常路径中遗漏
CloseHandle,RAII 封装(如std::unique_ptr配自定义 deleter)比裸调用更安全
真正的难点不在打开,而在确保整个生命周期里没有句柄泄漏、没有竞态窗口、也没有外部程序(比如编辑器、备份工具)偷偷打开它。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











