Unique File Identifier [duplicate]

2020-03-30 06:25发布

问题:

This question already has answers here:
Closed 7 years ago.

Possible Duplicate:
Unique file identifier in windows

I need to retrieve a unique identifier for certain files on the computer, and have only came across the Win32 GetFileInformationByHandle function. How can I accomplish this with the. NET framework?

Update: I need a persistent id that will not change if the file is moved, updated, renamed, etc.

Update2: How can the same be accomplished with folders?

回答1:

Here's some code from Ashley Henderson I copied from this answer. It implies two approaches which both return the same unique identifier.

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;   
       }
  }


回答2:

I assume the file format isn't under your control (otherwise, generatea UUID and store it there).

File Object ID's

NTFS supports object ID's on the file level, see FSCTL_CREATE_OR_GET_OBJECT_ID. I haven't used them to be able to recommend them, but it seems like a promising thing to explore.

As with alternate data streams (below), they can get lost when copying to a non-NTFS-medium ("usual" memory sticks, CD's, DVD's, flash cards, possibly even some USB disks. ...). Also, some applications might mess up when they recreate the file when saving.

Distributed Link Tracking Service

Distributed Link Tracking Service uses file object ID's for, well, tracking links to files, and repairing them when the file is moved.

AFAIK Distributed Link Tracking Service requires a domain controller for the server. Again, I have no practical experience with this.

On NTFS, you could also create and store a UUID in an alternate data stream.

Caveats:

  • Only available on NTFS, will not "survive" on other file systems
  • One white paper "the future of NTFS" considered killing them, but I fancy the thought there were some other features that could help you (unfortunately, I failed to dig that out)
  • I wouldn't want to create that for thousands of files of unknown origin and purpose. Though they "just work" on the file system level, some applications might mess up.

Some document formats - such as Office - allow custom document properties.

That's obviously limited, but similar mechanisms can be piggybacked onto other file types as well. (e.g. many image formats would allow adding / rewriting "custom" chunks that should be ignored by readers)

FileFromID vs. IDFromFile

Alll solutions except DLTS only allow IDFromFile lookup, i.e. locating a file that has moved (or was deleted) requires searching all potential drives.

For DLTS, if there's no "direct API" way, you could store a DLTS-enabled shortcut in an application-specific folder, and ~~hope for~~ expect the service to repair the shortcut when the file is moved.



回答3:

You can get the MD5 hash for the files, take this example:

string GetMD5HashFromFile(string fileName)
{
    FileStream file = new FileStream(fileName, FileMode.Open);
    MD5 md5 = new MD5CryptoServiceProvider();
    byte[] retVal = md5.ComputeHash(file);
    file.Close();

    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < retVal.Length; i++)
    {
        sb.Append(retVal[i].ToString("x2"));
    }
    return sb.ToString();
}

this will return a unique identifier for each file.