C#获取文件唯一标识 C#如何获取NTFS文件ID或inode

来源:这里教程网 时间:2026-02-21 17:42:01 作者:

c#获取文件唯一标识 c#如何获取ntfs文件id或inode

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<byte></byte>
,长度 16(8 字节 VolumeSerialNumber + 8 字节 FileId),不是
ulong
若你用
File.GetFileId(path)
后直接
.ToArray()
BitConverter.ToUInt64
取前 8 字节 → 错,那只是
FileId
低位,漏了卷序列号,无法防碰撞

真正安全的做法是始终把 16 字节当整体处理,或显式拆成两个

ulong
字段比对。

相关推荐