首页 文章

Windows中的唯一文件标识符

提问于
浏览
41

无论移动,重命名和内容修改如何,都有办法在文件的生命周期内唯一标识文件(可能还有目录)吗? (Windows 2000及更高版本) . 制作文件的副本应该为副本提供它自己的唯一标识符 .

我的应用程序将各种元数据与单个文件相关联 . 如果修改,重命名或移动文件,则能够自动检测和更新文件关联将非常有用 .

FileSystemWatcher可以提供通知这些更改的事件,但是如果快速发生许多文件系统事件,它会使用可以轻松填充(和事件丢失)的内存缓冲区 .

哈希是没有用的,因为文件的内容可以改变,因此哈希值会改变 .

我曾想过使用文件创建日期,但是有一些情况下这不是唯一的(即复制多个文件时) .

我也听说过NTFS中的文件SID(安全ID?),但我不确定这是否能满足我的要求 .

有任何想法吗?

4 回答

  • 0

    如果您调用GetFileInformationByHandle,您将获得BY_HANDLE_FILE_INFORMATION.nFileIndexHigh / Low中的文件ID . 该索引在卷中是唯一的,即使您移动文件(在卷内)或重命名它也保持不变 .

    如果您可以假设使用了NTFS,则可能还需要考虑使用备用数据流来存储元数据 .

  • 4

    这是返回唯一文件索引的示例代码 .

    ApproachA()是经过一番研究后得出的 . ApproachB()归功于Mattias和Rubens提供的链接中的信息 . 给定一个特定文件,两种方法都返回相同的文件索引(在我的基本测试期间) .

    来自MSDN的一些警告:

    对文件ID的支持是特定于文件系统的 . 文件ID不保证随着时间的推移是唯一的,因为文件系统可以自由地重用它们 . 在某些情况下,文件的文件ID可能会随时间而变化 . 在FAT文件系统中,文件ID是从包含目录的第一个簇和文件条目目录中的字节偏移量生成的 . 一些碎片整理产品会更改此字节偏移量 . (Windows内置碎片整理不会 . )因此,FAT文件ID可能会随时间而变化 . 重命名FAT文件系统中的文件也可以更改文件ID,但前提是新文件名长于旧文件名 . 在NTFS文件系统中,文件保留相同的文件ID,直到删除为止 . 您可以使用ReplaceFile函数将一个文件替换为另一个文件,而无需更改文件ID . 但是,替换文件的文件ID(而不是替换文件)将保留为生成的文件的文件ID .

    上面的第一个粗体评论让我担心 . 目前尚不清楚该声明是否仅适用于FAT,它似乎与第二个粗体文本相矛盾 . 我想进一步测试是确保的唯一方法 .

    [更新:在我的测试中,当文件从一个内部NTFS硬盘驱动器移动到另一个内部NTFS硬盘驱动器时,文件索引/ ID会发生变化 .

    public class WinAPI
        {
            [DllImport("ntdll.dll", SetLastError = true)]
            public static extern IntPtr NtQueryInformationFile(IntPtr fileHandle, ref IO_STATUS_BLOCK IoStatusBlock, IntPtr pInfoBlock, uint length, FILE_INFORMATION_CLASS fileInformation);
    
            public struct IO_STATUS_BLOCK
            {
                uint status;
                ulong information;
            }
            public struct _FILE_INTERNAL_INFORMATION {
              public ulong  IndexNumber;
            } 
    
            // Abbreviated, there are more values than shown
            public enum FILE_INFORMATION_CLASS
            {
                FileDirectoryInformation = 1,     // 1
                FileFullDirectoryInformation,     // 2
                FileBothDirectoryInformation,     // 3
                FileBasicInformation,         // 4
                FileStandardInformation,      // 5
                FileInternalInformation      // 6
            }
    
            [DllImport("kernel32.dll", SetLastError = true)]
            public static extern bool GetFileInformationByHandle(IntPtr hFile,out BY_HANDLE_FILE_INFORMATION lpFileInformation);
    
            public struct BY_HANDLE_FILE_INFORMATION
            {
                public uint FileAttributes;
                public FILETIME CreationTime;
                public FILETIME LastAccessTime;
                public FILETIME LastWriteTime;
                public uint VolumeSerialNumber;
                public uint FileSizeHigh;
                public uint FileSizeLow;
                public uint NumberOfLinks;
                public uint FileIndexHigh;
                public uint FileIndexLow;
            }
      }
    
      public class Test
      {
           public ulong ApproachA()
           {
                    WinAPI.IO_STATUS_BLOCK iostatus=new WinAPI.IO_STATUS_BLOCK();
    
                    WinAPI._FILE_INTERNAL_INFORMATION objectIDInfo = new WinAPI._FILE_INTERNAL_INFORMATION();
    
                    int structSize = Marshal.SizeOf(objectIDInfo);
    
                    FileInfo fi=new FileInfo(@"C:\Temp\testfile.txt");
                    FileStream fs=fi.Open(FileMode.Open,FileAccess.Read,FileShare.ReadWrite);
    
                    IntPtr res=WinAPI.NtQueryInformationFile(fs.Handle, ref iostatus, memPtr, (uint)structSize, WinAPI.FILE_INFORMATION_CLASS.FileInternalInformation);
    
                    objectIDInfo = (WinAPI._FILE_INTERNAL_INFORMATION)Marshal.PtrToStructure(memPtr, typeof(WinAPI._FILE_INTERNAL_INFORMATION));
    
                    fs.Close();
    
                    Marshal.FreeHGlobal(memPtr);   
    
                    return objectIDInfo.IndexNumber;
    
           }
    
           public ulong ApproachB()
           {
                   WinAPI.BY_HANDLE_FILE_INFORMATION objectFileInfo=new WinAPI.BY_HANDLE_FILE_INFORMATION();
    
                    FileInfo fi=new FileInfo(@"C:\Temp\testfile.txt");
                    FileStream fs=fi.Open(FileMode.Open,FileAccess.Read,FileShare.ReadWrite);
    
                    WinAPI.GetFileInformationByHandle(fs.Handle, out objectFileInfo);
    
                    fs.Close();
    
                    ulong fileIndex = ((ulong)objectFileInfo.FileIndexHigh << 32) + (ulong)objectFileInfo.FileIndexLow;
    
                    return fileIndex;   
           }
      }
    
  • 19

    请看这里:Unique file ids for Windows . 这也很有用:Unique ID for files on NTFS?

  • 30

    用户还提到了唯一的目录标识 . 这个过程比检索文件的唯一信息更复杂;但是,这是可能的 . 它要求你调用一个特定的标志 CREATE_FILE function . 使用该句柄,您可以在Ash的answer中调用 GetFileInformationByHandle 函数 .

    这也需要 kernel32.dll 导入:

    [DllImport("kernel32.dll", SetLastError = true)]
            public static extern SafeFileHandle CreateFile(
                string lpFileName,
                [MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
                [MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
                IntPtr securityAttributes,
                [MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition,
                uint dwFlagsAndAttributes,
                IntPtr hTemplateFile
            );
    

    稍后我会更多地充实这个答案 . 但是,通过上述相关答案,这应该开始有意义了 . 我最喜欢的资源是pinvoke,它帮助我了解.Net C#签名的可能性 .

相关问题