Identificador de archivo único en Windows

¿Hay alguna forma de identificar de manera única un archivo (y posiblemente directorios) durante el tiempo de vida del archivo independientemente de movimientos, cambios de nombre y modificaciones de contenido? (Windows 2000 y posterior). Hacer una copia de un archivo debe darle a la copia su propio identificador único.

Mi aplicación asocia varios metadatos con archivos individuales. Si se modifican, renombran o mueven los archivos, sería útil poder detectar y actualizar automáticamente las asociaciones de archivos.

FileSystemWatcher puede proporcionar eventos que informen de este tipo de cambios, sin embargo, utiliza un búfer de memoria que se puede llenar fácilmente (y se pierden eventos) si ocurren muchos eventos del sistema de archivos rápidamente.

Un hash no sirve porque el contenido del archivo puede cambiar, por lo que el hash cambiará.

Había pensado en usar la fecha de creación del archivo, sin embargo, hay algunas situaciones en las que esto no será único (es decir, cuando se copian varios archivos).

También he oído hablar de un archivo SID (¿Id. De seguridad?) En NTFS, pero no estoy seguro de si esto haría lo que estoy buscando.

¿Algunas ideas?

Si llama a GetFileInformationByHandle , obtendrá un ID de archivo en BY_HANDLE_FILE_INFORMATION.nFileIndexHigh / Low. Este índice es único dentro de un volumen y permanece igual incluso si mueve el archivo (dentro del volumen) o lo renombra.

Si puede suponer que se usa NTFS, también puede considerar el uso de flujos de datos alternativos para almacenar los metadatos.

Aquí hay un código de muestra que devuelve un índice de archivo único.

ApproachA () es lo que surgió después de un poco de investigación. ApproachB () es gracias a la información en los enlaces proporcionados por Mattias y Rubens. Dado un archivo específico, ambos enfoques devuelven el mismo índice de archivo (durante mi prueba básica).

Algunas advertencias de MSDN:

El soporte para archivos ID es específico del sistema de archivos. No se garantiza que las ID de los archivos sean únicas a lo largo del tiempo, ya que los sistemas de archivos pueden reutilizarlas. En algunos casos, la identificación del archivo de un archivo puede cambiar con el tiempo.

En el sistema de archivos FAT, el ID del archivo se genera a partir del primer clúster del directorio que lo contiene y del desplazamiento del byte dentro del directorio de la entrada del archivo. Algunos productos de desfragmentación cambian esta compensación de bytes. (La desfragmentación de Windows no lo hace). Por lo tanto, un ID de archivo FAT puede cambiar con el tiempo. Renombrar un archivo en el sistema de archivos FAT también puede cambiar la ID del archivo, pero solo si el nuevo nombre de archivo es más largo que el anterior.

En el sistema de archivos NTFS, un archivo mantiene el mismo ID de archivo hasta que se elimina . Puede reemplazar un archivo con otro archivo sin cambiar la ID del archivo utilizando la función Reemplazar archivo. Sin embargo, el ID del archivo de reemplazo, no el archivo reemplazado, se conserva como el ID del archivo resultante.

El primer comentario en negrita anterior me preocupa. No está claro si esta afirmación se aplica solo a FAT, parece contradecir el segundo texto en negrita. Supongo que realizar más pruebas es la única forma de estar seguro.

[Actualización: en mis pruebas, el índice / id del archivo cambia cuando un archivo se mueve de un disco duro NTFS interno a otro disco duro NTFS interno.]

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

Por favor, eche un vistazo aquí: ids únicos de archivos para Windows . Esto también es útil: ID única para archivos en NTFS?

El usuario también menciona una identificación de directorio única. Ese proceso es un poco más intrincado que recuperar información única para un archivo; sin embargo, es posible. Requiere que llame a la función CREATE_FILE apropiada que es una bandera en particular. Con ese identificador, puede llamar a la función GetFileInformationByHandle en la respuesta de Ash.

Esto también requiere una importación de 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 ); 

Leeré esta respuesta un poco más, más tarde. Pero, con la respuesta vinculada anterior, esto debería comenzar a tener sentido. Un nuevo recurso favorito mío es Pinvoke, que me ha ayudado con las posibilidades de firmas de .Net C #.