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;
}
}
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
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. " –
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