最可靠方式是尝试以FileShare.None打开文件并捕获IOException:HResult为0x80070020(ERROR_SHARING_VIOLATION)或0x80070005(ERROR_ACCESS_DENIED)即表示被占用;FileShare.Read因允许并发读取而不可靠;Windows下应优先用HResult而非Message判断,跨平台需做系统检测。
判断文件是否被占用的最可靠方式是尝试打开它
windows 下没有公开、稳定、实时的 api 能直接查询“某文件此刻被哪个进程锁着”,filestream 的异常才是唯一可信信号。别信 file.exists 或 file.getattributes ——它们完全不反映独占状态。
实操建议:
- 用
File.Open或new FileStream以FileMode.Open+FileAccess.Read+FileShare.None尝试打开,这是模拟“独占读”的最小代价操作 - 捕获
IOException,并检查InnerException是否为UnauthorizedAccessException或错误码是否为 32(ERROR_SHARING_VIOLATION)或 5(ERROR_ACCESS_DENIED) - 不要依赖
Exception.Message字符串匹配——不同系统语言下内容会变,只看异常类型和HResult - 示例片段:
try { using (var fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.None)) { return false; } } catch (IOException ex) { if (ex.HResult == unchecked((int)0x80070020) || ex.HResult == unchecked((int)0x80070005)) return true; throw; }
FileShare.None 和 FileShare.Read 的区别直接影响判断逻辑
很多代码误用 FileShare.Read 去“探测”占用,结果永远返回“空闲”——因为多数写入进程(如记事本、Excel、日志库)默认允许其他进程并发读取。
关键点:
-
FileShare.None:要求完全独占,任何已打开的句柄(无论读/写/读写)都会导致失败 → 适合判断“是否被任意方式占用” -
FileShare.Read:只拒绝其他写入,但放行读取 → 如果目标文件正被另一个程序以FileAccess.Write打开,它仍会成功,无法发现冲突 - 日志类场景(如
NLog、Serilog)常以FileAccess.Write+FileShare.Read持有文件,此时仅用FileShare.Read探测必然失效
捕获 IOException 时容易忽略 HResult 的平台差异
.NET Core/.NET 5+ 在非 Windows 平台(如 Linux)下,IOException.HResult 总是 0,不能用于判断;但 Windows 上它是唯一稳定标识错误类型的字段。
安全做法:
- Windows 环境下优先用
ex.HResult == unchecked((int)0x80070020)判断共享冲突(即错误码 32) - 避免用
ex.Message.Contains("used by another process")——中文系统会变成“正由另一进程使用”,西班牙语更不可靠 - 跨平台项目若需兼容,应封装平台检测逻辑:
Environment.OSVersion.Platform == PlatformID.Win32NT再走 HResult 分支,否则降级为简单重试或超时策略 - 注意:.NET 6+ 中
ex.InnerException可能为Win32Exception,其NativeErrorCode更直观,但仅限 Windows
轮询判断文件释放时,Sleep 时间不是越短越好
高频轮询(比如 Thread.Sleep(1))不仅浪费 CPU,还可能因系统调度延迟导致“刚判断完就又被占上”,反而错过真正可用窗口。
合理节奏:
- 首次失败后等待 10–50ms,再试一次;若仍失败,指数退避(如 100ms → 200ms → 400ms)
- 总超时建议设为 1–3 秒,长于这个时间大概率是人为锁定(如用户正在编辑),继续等意义不大
- 避免在 UI 线程中 Sleep ——改用
await Task.Delay防止界面冻结 - 特别注意:某些杀毒软件会在文件读取瞬间加临时锁,持续几毫秒,太激进的轮询反而更容易撞上它
真正的难点不在“怎么试”,而在“试多少次、隔多久、何时放弃”——这得看你面对的是日志文件、配置文件,还是用户双击打开的 Excel。没有银弹,只有权衡。










