C# Windows 中的唯一文件标识符

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1866454/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 21:29:29  来源:igfitidea点击:

Unique file identifier in windows

c#.netwindowsfilesystems

提问by Ash

Is there are way to uniquely identify a file (and possibly directories) for the lifetime of the file regardless of moves, renames and content modifications? (Windows 2000 and later). Making a copy of a file should give the copy it's own unique identifier.

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

My application associates various meta-data with individual files. If files are modified, renamed or moved it would be useful to be able to automatically detect and update file associations.

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

FileSystemWatcher can provide events that inform of these sorts of changes, however it uses a memory buffer that can be easily filled (and events lost) if many file system events occur quickly.

FileSystemWatcher 可以提供通知这些类型更改的事件,但是它使用一个内存缓冲区,如果许多文件系统事件快速发生,该缓冲区可以很容易地填充(并且事件丢失)。

A hash is no use because the content of the file can change, and so the hash will change.

散列没有用,因为文件的内容可以更改,因此散列也会更改。

I had thought of using the file creation date, however there are a few situations where this will not be unique (ie. when multiple files are copied).

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

I've also heard of a file SID (security ID?) in NTFS, but I'm not sure if this would do what I'm looking for.

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

Any ideas?

有任何想法吗?

采纳答案by Mattias S

If you call GetFileInformationByHandle, you'll get a file ID in BY_HANDLE_FILE_INFORMATION.nFileIndexHigh/Low. This index is unique within a volume, and stays the same even if you move the file (within the volume) or rename it.

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

If you can assume that NTFS is used, you may also want to consider using Alternate Data Streams to store the metadata.

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

回答by Rubens Farias

Please take a look here: Unique file ids for Windows. This is also useful: Unique ID for files on NTFS?

请查看此处:Windows 的唯一文件 ID。这也很有用:NTFS 上文件的唯一 ID?

回答by Ash

Here's sample code that returns a unique File Index.

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

ApproachA() is what I came up with after a bit of research. ApproachB() is thanks to information in the links provided by Mattias and Rubens. Given a specific file, both approaches return the same file index (during my basic testing).

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

Some caveats from MSDN:

MSDN 的一些警告:

Support for file IDs is file system-specific. File IDs are not guaranteed to be unique over time, because file systems are free to reuse them. In some cases, the file ID for a file can change over time.

In the FAT file system, the file ID is generated from the first cluster of the containing directory and the byte offset within the directory of the entry for the file. Some defragmentation products change this byte offset. (Windows in-box defragmentation does not.) Thus, a FAT file ID can change over time. Renaming a file in the FAT file system can also change the file ID, but only if the new file name is longer than the old one.

In the NTFS file system, a file keeps the same file ID until it is deleted. You can replace one file with another file without changing the file ID by using the ReplaceFile function. However, the file ID of the replacement file, not the replaced file, is retained as the file ID of the resulting file.

对文件 ID 的支持是特定于文件系统的。文件 ID 不能保证随着时间的推移是唯一的,因为文件系统可以自由地重用它们。在某些情况下,文件的文件 ID 可能会随着时间而改变。

在 FAT 文件系统中,文件 ID 是从包含目录的第一个簇和文件条目的目录内的字节偏移量生成的。某些碎片整理产品会更改此字节偏移量。(Windows 内置碎片整理不会。)因此,FAT 文件 ID 会随着时间而改变。重命名 FAT 文件系统中的文件也可以更改文件 ID,但前提是新文件名比旧文件名长。

在 NTFS 文件系统中,一个文件在被删除之前保持相同的文件 ID。您可以使用 ReplaceFile 函数将一个文件替换为另一个文件,而无需更改文件 ID。但是,替换文件的文件 ID,而不是替换文件,作为结果文件的文件 ID 保留。

The first bolded comment above worries me. It's not clear if this statement applies to FAT only, it seems to contradict the second bolded text. I guess further testing is the only way to be sure.

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

[Update: in my testing the file index/id changes when a file is moved from one internal NTFS hard drive to another internal NTFS hard drive.]

[更新:在我的测试中,当文件从一个内部 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;   
       }
  }

回答by Thomas

The user also mentions unique directory identification. That process is a bit more convoluted than retrieving unique information for a file; however, it is possible. It requires you to call the appropriate CREATE_FILEfunctionwhich a particular flag. With that handle, you can call the GetFileInformationByHandlefunction in Ash's answer.

用户还提到了唯一的目录标识。这个过程比检索文件的唯一信息要复杂一些。然而,这是可能的。它要求您调用特定标志的适当CREATE_FILE函数。使用该句柄,您可以调用GetFileInformationByHandleAsh's answer 中的函数。

This also requires a kernel32.dllimport:

这也需要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
        );

I'll flesh out this answer a bit more, later. But, with the above linked answer, this should begin to make sense. A new favorite resource of mine is pinvokewhich has helped me with .Net C# signature possibilities.

稍后我会详细说明这个答案。但是,有了上面链接的答案,这应该开始有意义了。我最喜欢的一个新资源是pinvoke,它帮助我实现了 .Net C# 签名的可能性。