Уникальный идентификатор файла в окнах
Есть ли способ однозначно идентифицировать файл (и, возможно, каталоги) для времени жизни файла независимо от ходов, переименований и модификаций контента? (Windows 2000 и более поздние версии). Создание копии файла должно предоставить копии уникального идентификатора.
Мое приложение связывает различные метаданные с отдельными файлами. Если файлы будут изменены, переименованы или перемещены, было бы полезно иметь возможность автоматически обнаруживать и обновлять ассоциации файлов.
FileSystemWatcher может предоставлять события, которые сообщают об этих изменениях, однако он использует буфер памяти, который можно легко заполнить (и потерять события), если происходит много событий файловой системы.
Хеш бесполезен, потому что содержимое файла может измениться, и поэтому хеш изменится.
Я подумал о том, чтобы использовать дату создания файла, однако есть несколько ситуаций, когда это не будет уникальным (т.е. при копировании нескольких файлов).
Я также слышал о файле SID (идентификатор безопасности?) в NTFS, но я не уверен, будет ли это делать то, что я ищу.
Любые идеи?
Ответы
Ответ 1
Если вы вызываете GetFileInformationByHandle, вы получите идентификатор файла в BY_HANDLE_FILE_INFORMATION.nFileIndexHigh/Low. Этот индекс уникален в пределах тома и остается таким же, даже если вы перемещаете файл (в пределах тома) или переименовываете его.
Если вы можете предположить, что NTFS используется, вы также можете рассмотреть возможность использования альтернативных потоков данных для хранения метаданных.
Ответ 2
Здесь пример кода, который возвращает уникальный индекс файла.
ApproachA() - это то, что я придумал после нескольких исследований. ApproachB() благодаря информации в ссылках, предоставленных Mattias и Rubens. Учитывая определенный файл, оба подхода возвращают один и тот же индекс файла (во время моего основного тестирования).
Некоторые предостережения из MSDN:
Поддержка идентификаторов файлов - файл конкретная система. Идентификаторы файлов не являются гарантированно быть уникальным с течением времени, потому что файловые системы могут повторно использовать их. В некоторых случаях идентификатор файла для файл может меняться со временем.
В файловой системе FAT идентификатор файла созданный из первого кластера содержащая директория и байт смещение в каталоге запись для файла. Некоторые продукты дефрагментации меняют это смещение байта. (Windows in-box дефрагментации нет.) Таким образом, FAT Идентификатор файла может меняться со временем. Переименование файл в файловой системе FAT также может измените идентификатор файла, но только если новое имя файла длиннее старого один.
В файловой системе NTFS файл сохраняется тот же идентификатор файла, пока он не будет удален. Вы можете заменить один файл другим файл без изменения идентификатора файла используя функцию ReplaceFile. Однако идентификатор файла файл замены, а не замененный файл, сохраняется как идентификатор файла результирующий файл.
Первый смелый комментарий выше меня беспокоит. Неясно, относится ли это утверждение только к FAT, это противоречит второму выделенному тексту. Я предполагаю, что дальнейшее тестирование - единственный способ быть уверенным.
[Обновление: при тестировании индекс файла /id изменяется, когда файл перемещается с одного внутреннего жесткого диска NTFS на другой внутренний жесткий диск 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;
}
}
Ответ 3
Обратите внимание: Уникальные идентификаторы файлов для Windows. Это также полезно: Уникальный идентификатор для файлов в NTFS?
Ответ 4
Пользователь также упоминает уникальную идентификацию каталога. Этот процесс немного запутан, чем получение уникальной информации для файла; однако это возможно. Это требует, чтобы вы вызывали соответствующую CREATE_FILE
функцию, которая имеет определенный флаг. С помощью этого дескриптора вы можете вызвать функцию GetFileInformationByHandle
в Ash answer.
Для этого также требуется 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
);
Я повторю этот ответ немного позже. Но, с приведенным выше ответным ответом, это должно начать иметь смысл. Мой новый любимый ресурс pinvoke, который помог мне с возможностями .Net С#.