2009-12-08 7 views
36

Czy istnieje sposób na jednoznaczną identyfikację pliku (i ewentualnie katalogów) przez cały okres istnienia pliku, niezależnie od ruchów, nazw i modyfikacji zawartości? (Windows 2000 i nowsze). Sporządzenie kopii pliku powinno dać kopii jego własny unikalny identyfikator.Unikalny identyfikator pliku w oknach

Moja aplikacja kojarzy różne metadane z poszczególnymi plikami. Jeśli pliki są modyfikowane, zmieniane lub przenoszone, warto automatycznie wykrywać i aktualizować powiązania plików.

FileSystemWatcher może udostępniać zdarzenia informujące o tego rodzaju zmianach, jednak używa bufora pamięci, który można łatwo wypełnić (i utracone zdarzenia), jeśli wiele zdarzeń systemu plików pojawi się szybko.

Hash nie ma sensu, ponieważ zawartość pliku może się zmienić, a więc wartość mieszania ulegnie zmianie.

Myślałem o używaniu daty utworzenia pliku, jednak istnieje kilka sytuacji, w których nie będzie to unikatowe (tj. Gdy kopiowanych jest wiele plików).

Słyszałem również o pliku SID (identyfikator bezpieczeństwa?) W systemie plików NTFS, ale nie jestem pewien, czy zrobiłby to, czego szukam.

Wszelkie pomysły?

Odpowiedz

17

Jeśli zadzwonisz pod numer GetFileInformationByHandle, otrzymasz identyfikator pliku w BY_HANDLE_FILE_INFORMATION.nFileIndexHigh/Low. Ten indeks jest unikalny w obrębie woluminu i pozostaje taki sam, nawet po przeniesieniu pliku (w obrębie woluminu) lub zmianie jego nazwy.

Jeśli możesz założyć, że NTFS jest używany, możesz również rozważyć użycie alternatywnych strumieni danych do przechowywania metadanych.

+0

Dzięki za link. W rzeczywistości znalazłem inne wywołanie API, które zwraca ten sam identyfikator, ale wymaga nieco więcej pracy. Mogę opublikować kod, bo wymyśliłem. Alternatywny strumień danych również może być przydatny, ponieważ jedyne miejsce, z którym spotykam się z FAT, znajduje się na kluczach USB/napędach zewnętrznych. Jednak słyszałem, że niektóre programy antywirusowe/zabezpieczające mogą mieć problem z dodawaniem ukrytych danych do plików. – Ash

+6

Dokumentacja dla GetFileInformationByHandle mówi: "nFileIndexLow: Część niskiego rzędu unikalnego identyfikatora powiązanego z plikiem Ta wartość jest użyteczna TYLKO PODCZAS PLIKU JEST OTWARTYM co najmniej jednym procesem. Jeśli żaden proces go nie otwiera, indeks może zmienić się przy następnym otwarciu pliku. " –

+0

Hm, ta klauzula nie wydaje się być w dokumentacji na MSDN (już?). Empirycznie widzę, że indeks plików pozostaje unikalny po ponownym uruchomieniu (w systemie plików NTFS). – sqweek

28

Oto przykładowy kod, który zwraca unikalny indeks plików.

ApproachA() jest tym, co wymyśliłem po kilku badaniach. ApproachB() jest dzięki informacjom zawartym w linkach dostarczonych przez Mattiasa i Rubensa. Biorąc pod uwagę określony plik, oba podejścia zwracają ten sam indeks pliku (podczas moich podstawowych testów).

Niektóre ostrzeżenia z MSDN:

Wsparcie dla identyfikatorów plików jest plik od systemu. Identyfikatory plików nie są gwarantowane jako unikalne w czasie, , ponieważ systemy plików mogą ponownie używać . W niektórych przypadkach identyfikator pliku dla pliku może się zmieniać z czasem.

w systemie plików FAT, identyfikator pliku jest generowane od pierwszego klastra zawierający katalog i bajtów przesunięcie w katalogu wpisu pliku. Niektóre produkty do defragmentacji zmieniają ten offset bajtowy . (Defragmentacja systemu Windows w wersji nie jest możliwa.) Identyfikator pliku FAT może się zmieniać z czasem. Zmiana nazwy plik w systemie plików FAT może także zmienić numer ID pliku, ale tylko wtedy, gdy nowa nazwa pliku jest dłuższa niż stara nazwa .

W systemie plików NTFS plik zachowuje ten sam identyfikator pliku, dopóki nie zostanie usunięty. Można zastąpić jeden plik innym plikiem bez zmiany identyfikatora pliku przez przy użyciu funkcji ReplaceFile. Identyfikator pliku pliku , a nie zastąpiony plik , jest zachowywany jako plik o numerze wynikowego pliku.

Pierwszy pogrubiony komentarz powyżej mnie martwi. Nie jest jasne, czy to stwierdzenie odnosi się tylko do FAT, wydaje się sprzeczne z drugim pogrubionym tekstem. Sądzę, że dalsze testy to jedyny sposób, by się upewnić.

[Update. W moich próbach plików zmian index/id, gdy plik jest przenoszony z jednego wewnętrznego dysku NTFS do innego wewnętrznego dysku NTFS]

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

Dobrze, wypróbowałem to, ale znalazłem jeden problem: nie działa dla plików takich jak pakiet Microsoft Office (doc, docx, xls ...), ponieważ za każdym razem, gdy wprowadzałeś zmiany, Office zwykle usuwa plik i tworzy nowy plik do zastąpienia, wynik ten zmienił numer referencyjny, chociaż numer referencyjny wciąż jest unikalny. Nie można wykryć zmian w tych plikach, a być może niektóre inne programy również będą miały podobne podejście. Więc domyślam się, że wrócę do mojej metody CreationTime ... – VHanded

+0

FWIW, nie mogłem dostać Ashley Henderson's ApproachA do pracy, ale ApproachB działało. Udało mi się potwierdzić notatkę VHandeda, że ​​FileID zmienia się podczas modyfikowania dokumentu Word (docx), co jest zbyt złe, ponieważ pliki Office są tymi, które chciałem śledzić. –

+0

"za każdym razem, gdy wprowadzałeś zmiany, Urząd usuwa plik" ... niech to diabli, AutoCAD też to robi. Powrót do 'FileSystemWatcher' –

0

Użytkownik wspomina również unikalną identyfikację abonenta. Ten proces jest nieco bardziej skomplikowany niż pobieranie unikalnych informacji dla pliku; jest to jednak możliwe. Wymaga on wywołania odpowiedniej konkretnej flagi pod numerem CREATE_FILEfunction. Za pomocą tego uchwytu możesz wywołać funkcję GetFileInformationByHandle w Ashto answer.

Wymaga to również kernel32.dll import:

 [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 
     ); 

będę ciele tę odpowiedź nieco więcej później. Ale z powyższą odpowiedzią powinno to zacząć mieć sens. Nowym moim ulubionym źródłem jest pinvoke, który pomógł mi z możliwościami podpisu w .Net C#.

Powiązane problemy