
用 GetFileInformationByHandle 读取 NTFS 文件 ID(FileIndex)
Windows NTFS 下的“文件唯一标识”实际是 FILE_ID_INFO 中的 FileId,它由 8 字节高位(VolumeSerialNumber)和 8 字节低位(FileId)组成,跨卷不唯一,但在同一卷内可稳定标识一个文件(即使重命名、移动)。C# 没有直接封装该结构的 API,必须调用 Win32:GetFileInformationByHandle 配合 FILE_ID_INFO。
关键点:
- 必须使用
FileOptions.Asynchronous或确保句柄可被查询(FileAccess.Read足够,无需写权限) - 目标文件需在 NTFS 卷上;FAT32/exFAT 返回
0或无效值 -
FileId是逻辑 ID,不是 inode —— Windows 不暴露传统 Unix inode,但语义等价
var h = CreateFile(path, FileAccess.Read, FileShare.Read, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
if (h == INVALID_HANDLE_VALUE) throw new IOException();
try {
var info = new FILE_ID_INFO();
if (!GetFileInformationByHandleEx(h, FileInfoByHandleClass.FileIdInfo, ref info, sizeof(FILE_ID_INFO)))
throw new IOException($"GetFileInformationByHandleEx failed: {Marshal.GetLastWin32Error()}");
return info.FileId.ToUInt64(); // 注意:低位在前,高位在后,ToUInt64 取的是低8字节
}
GetFileInformationByHandleEx 的 FileIdInfo 和 FileNameInfo 区别
容易混淆的是:FileNameInfo 返回的是路径名(含相对路径),而 FileIdInfo 才返回真正稳定的卷内 ID。前者会随重命名/移动变化,后者不会。
常见误用场景:
- 用
GetFileInformationByHandleEx(..., FileNameInfo, ...)当作唯一标识 → 错,只是当前路径快照 - 没检查
dwFileType就假设是 NTFS → FAT32 下FileId全为 0,需提前验证卷格式(可用GetVolumeInformation) - 直接把
FileId当作 64 位整数比较 → 必须同时比对VolumeSerialNumber,否则跨卷碰撞风险高
替代方案:用 USN Journal 或 ObjectID?
ObjectID 是可设置的 GUID,需手动调用 SetFileInformationByHandle 设置,且默认为空;它不自动分配,也不随文件创建产生,不适合“获取已有文件唯一 ID”的需求。
USN Journal 记录变更序号(Usn),但它不是文件标识,而是操作日志编号,同一文件多次修改对应多个 USN,不可用于识别文件本身。
所以——
- 要“只读、不改、跨重命名/移动仍一致” → 坚持用
FileIdInfo+VolumeSerialNumber - 要“跨卷全局唯一” → 必须自己拼接
{VolumeSerialNumber}-{FileId}并以字符串或结构体形式保存,不能只存FileId - 要“加密哈希式不可逆标识” → 可考虑
GetFileHash(如 SHA256),但性能差、内容变则 ID 变,语义不同
注意 FILE_ID_INFO 在 .NET 5+ 的跨平台陷阱
.NET 5+ 引入了 File.GetFileId(仅 Windows),但它内部就是封装了 GetFileInformationByHandleEx,行为一致。但要注意:
- 该方法在非 Windows 平台抛
PlatformNotSupportedException,不会静默失败 - 它返回的是
ReadOnlySpan,长度 16(8 字节 VolumeSerialNumber + 8 字节 FileId),不是ulong - 若你用
File.GetFileId(path)后直接.ToArray()再BitConverter.ToUInt64取前 8 字节 → 错,那只是FileId低位,漏了卷序列号,无法防碰撞
真正安全的做法是始终把 16 字节当整体处理,或显式拆成两个 ulong 字段比对。










