USN Journal 是 NTFS 卷级底层变更日志,记录精确时间戳、文件 ID、操作类型等完整元数据;FileSystemWatcher 仅捕获用户态可见事件且易丢件,无法满足审计与高可靠同步需求。

USN Journal 是什么,为什么不能直接用 FileSystemWatcher
它不是普通文件系统事件——FileSystemWatcher 基于 Windows 的通知机制(如 ReadDirectoryChangesW),只捕获“用户态可见”的变更(比如创建、重命名),且容易丢事件、不记录删除源路径、无法追溯已发生的操作。USN Journal(Update Sequence Number Journal)是 NTFS 卷级的底层日志,每条记录包含精确时间戳、文件 ID、父目录 ID、变更原因(USN_REASON_FILE_CREATE 等)、旧/新文件名(重命名时)、甚至硬链接变动。要审计、取证、或实现高可靠性同步,必须绕过 .NET 封装,直读 USN 日志。
如何打开并读取 USN Journal(C# 调用 DeviceIoControl)
核心是向卷句柄发送 FSCTL_QUERY_USN_JOURNAL 和 FSCTL_READ_USN_JOURNAL 控制码。关键点不是“能不能调”,而是“怎么避免权限失败和结构错位”:
- 必须以管理员权限运行,且打开卷时需指定
GENERIC_READ+FILE_SHARE_READ | FILE_SHARE_WRITE,路径格式为\\.\C:(注意双反斜杠+点+盘符+冒号) -
USN_JOURNAL_DATA结构体字段顺序、对齐方式必须严格匹配 Windows SDK 定义(尤其UsnJournalID是ulong,不是long;FirstUsn/NextUsn同理) - 首次读取要用
STARTING_USN0,后续用上次返回的NextUsn继续拉取,不能简单加 1——USN 不连续,跳过无效值会直接失败 - 每次
FSCTL_READ_USN_JOURNAL返回的是变长USN_RECORD_V2或V3数组,需按RecordLength字段逐个偏移解析,不能用Marshal.PtrToStructure一次性转整个缓冲区
示例片段(关键逻辑):
var buffer = Marshal.AllocHGlobal(64 * 1024);
uint bytesReturned;
DeviceIoControl(hVolume, FSCTL_READ_USN_JOURNAL, in readInput, (uint)Marshal.SizeOf<READ_USN_JOURNAL_DATA_V0>(),
buffer, 64 * 1024, out bytesReturned, IntPtr.Zero);
// 解析:从 buffer 开始,循环读取每个 USN_RECORD_V2.Length,再 Marshal.PtrToStructure(..., typeof(USN_RECORD_V2))
常见崩溃和静默失败场景
不是代码报错才叫问题——多数 USN 读取失败是无声的:
-
ERROR_INVALID_PARAMETER:最常因READ_USN_JOURNAL_DATA_V0中ReasonMask设为 0 导致(必须至少设一个原因,如USN_REASON_MASK_FILE_CREATE) -
ERROR_JOURNAL_ENTRY_DELETED:卷上日志被清空(手动执行fsutil usn deletejournal /d C:或空间不足自动清理),此时StartUsn已失效,需重新查FSCTL_QUERY_USN_JOURNAL获取最新FirstUsn - 结构体字段类型错(比如把
Usn当成int)→ 解析出负数 USN → 下次请求传负值 →ERROR_INVALID_PARAMETER,但错误位置难定位 - 未处理
USN_REASON_RENAME_OLD_NAME和USN_REASON_RENAME_NEW_NAME成对出现的情况——重命名实际记两条记录,漏掉任一条就丢失源/目标路径
性能与稳定性底线提醒
USN Journal 不是流式 API,它是离散快照式日志。单次读取上限约 64KB,大变更爆发时可能一次拉不完,需循环直到 NextUsn == LastUsn;更关键的是:NTFS 不保证日志持久化——如果系统异常关机,最后几秒的 USN 记录可能丢失。真正需要强一致性的场景(如备份校验),不能只依赖 USN Journal,得结合 GetFileInformationByHandleEx 查询 FILE_ID_INFO 和时间戳做二次比对。另外,FSCTL_READ_USN_JOURNAL 是阻塞调用,别在 UI 线程里直接用。










