您的位置:首页 >c++如何获取Windows下任意文件的唯一文件标识符【技巧】
发布于2026-05-03 阅读(0)
扫一扫,手机访问
Windows下稳定标识文件的是FILE_ID,由VolumeSerialNumber与FileId组合构成;需用GetFileInformationByHandleEx(FileIdInfo)获取,句柄须以FILE_READ_ATTRIBUTES权限加FILE_FLAG_BACKUP_SEMANTICS打开。

在Windows系统里,想稳定地追踪一个文件,光靠文件名和路径可不够。文件重命名了怎么办?移动到同个磁盘的其他文件夹里呢?这时候,真正能扛得住这些变化的,是内核层面的FILE_ID。
这个标识符由两部分“拼”起来:dwVolumeSerialNumber(卷序列号)和FileId(一个ULARGE_INTEGER)。这里有个常见的误区:很多人习惯用GetFileInformationByHandle返回的nFileIndexHigh和nFileIndexLow。其实,这只是旧式FAT/NTFS的文件索引,一旦遇到ReFS文件系统或者某些符号链接场景,它就可能“掉链子”。真正现代且可靠的做法,是使用GetFileInformationByHandleEx配合FileIdInfo这个信息类。
GetFileInformationByHandleEx获取FILE_ID具体怎么操作?关键在于两步:拿到正确的句柄,然后请求正确的信息。
首先,句柄的打开方式有讲究。权限只需要FILE_READ_ATTRIBUTES,完全不需要GENERIC_READ(这意味着你甚至不用有读取文件内容的权限)。但一个高频错误是,忘了加上FILE_FLAG_BACKUP_SEMANTICS这个标志位。对于目录,不加它肯定失败;对于普通文件,加上它能有效绕过一些访问控制列表(ACL)的拦截,算是“最佳实践”。
流程可以拆解为以下几步:
CreateFile,将dwDesiredAccess参数设为FILE_READ_ATTRIBUTES,并在dwFlagsAndAttributes中包含FILE_FLAG_BACKUP_SEMANTICS。INVALID_HANDLE_VALUE,确认有效后,再调用GetFileInformationByHandleEx。FileIdInfo,系统会将信息填充到FILE_ID_INFO结构体中。记住,只有VolumeSerialNumber和FileId组合在一起,才是那个跨系统重启都稳定的唯一ID。
HANDLE h = CreateFile(L"C:\test.txt", FILE_READ_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
if (h != INVALID_HANDLE_VALUE) {
FILE_ID_INFO info = {};
if (GetFileInformationByHandleEx(h, FileIdInfo, &info, sizeof(info))) {
// info.VolumeSerialNumber + info.FileId 为唯一标识
}
CloseHandle(h);
}
GetFileInformationByHandle的nFileIndex你可能会问,旧API不是也能拿到一个索引号吗?为什么非要换新的?
根本原因在于“稳定性”和“普适性”。GetFileInformationByHandle返回的nFileIndex,在NTFS上确实对应文件的MFT(主文件表)索引号。但问题在于,它不保证持久不变——比如系统经过一次磁盘碎片整理,这个索引就可能发生变化。更关键的是,在ReFS这类现代文件系统上,它基本就“失灵”了,nFileIndexHigh会恒为0。
反观FileIdInfo提供的信息,是由文件系统驱动本身保证的全局唯一且持久的标识。只要文件没被物理删除,这个ID就不会变。当然,新技术有门槛:
nFileIndex在Windows Server 2012 R2及更高版本的ReFS卷上已无意义。nFileIndex也可能被绕过。FileIdInfo要求的最低系统版本是Windows 8或Server 2012,这意味着它无法在XP或Windows 7上使用。掌握了核心API,在实际编码中还得留心几个“边界情况”,否则很容易踩坑。
首先是路径解析问题。遇到符号链接、挂载点或重解析点,CreateFile的默认行为是“解引用”,即拿到目标文件的ID。如果你想要的是链接文件本身的ID,就需要在打开时额外加上FILE_FLAG_OPEN_REPARSE_POINT标志。
其次是网络路径。访问UNC路径时,必须确保网络驱动器已映射,或者SMB签名已启用。否则,尝试获取FILE_ID很可能失败,返回空值或ERROR_NOT_SUPPORTED错误。
立即学习“C++免费学习笔记(深入)”;
FileIdInfo返回ERROR_NOT_SUPPORTEDGetFileInformationByHandle并结合ftLastWriteTime等属性进行辅助校验。FILE_FLAG_BACKUP_SEMANTICS标志是必需的,缺少它会导致CreateFile直接失败。FileIdInfo,此时应检查GetLastError()是否为ERROR_ACCESS_DENIED。最后,必须明确FILE_ID的本质:它既不是基于文件内容计算出的哈希值,也不依赖文件路径。它是文件系统分配的内部元数据ID。只要文件没被删除、没有跨卷移动,这个ID就是唯一的。但千万别把它当作加密或安全凭证来用——因为它可以被枚举,本身也不具备防篡改特性。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
9