Aquí hay un código de ejemplo 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:
Soporte para identificadores de archivo es un archivo específicos del sistema. Los ID de archivo no son con la garantía de ser únicos en el tiempo, porque los sistemas de archivos son libres de reutilizarlos . En algunos casos, el ID del archivo puede cambiar con el tiempo.
En el sistema de archivos FAT, el identificador de archivo es generada a partir del primer grupo de el directorio que contiene y el byte desplazamiento dentro del directorio de la entrada para el archivo. Algunos productos de desfragmentación cambian este offset de bytes. (La desfragmentación de Windows no lo hace.) Por lo tanto, un ID de archivo FAT puede cambiar con el tiempo. Renombrando un archivo en el sistema de archivos FAT también puede cambiar el 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 con el mismo ID de archivo hasta que se elimina. Puede reemplazar un archivo con otro archivo sin cambiar el ID de archivo por utilizando la función Reemplazar archivo. Sin embargo, el archivo ID del archivo de reemplazo , no el archivo reemplazado , se conserva como el ID de archivo del archivo resultante.
Me preocupa el primer comentario en negrita de arriba. 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 mi prueba del archivo de cambios de índice/ID cuando se mueve un archivo de un disco duro interno de NTFS a otro disco duro interno 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;
}
}
Gracias por el enlace. De hecho, encontré otra llamada API que devuelve el mismo ID pero requiere un poco más de trabajo. Puede que publique el código para el que se me ocurrió. Un flujo de datos alternativo podría ser útil también, ya que el único lugar donde se encuentra FAT es en las llaves USB/unidades externas. Sin embargo, he oído que algunos antivirus/software de seguridad pueden tener problemas para agregar datos ocultos a los archivos. – Ash
La documentación de GetFileInformationByHandle dice: "nFileIndexLow: parte de bajo orden de un identificador único que está asociado a un archivo. Este valor es ÚNICAMENTE MIENTRAS EL ARCHIVO ESTÁ ABIERTO por al menos un proceso. Si ningún proceso lo tiene abierto, el índice puede cambiar la próxima vez que se abra el archivo ". –
Hm, esta cláusula no parece estar en la documentación en MSDN (¿ya?). Empíricamente, veo que el índice del archivo permanece único durante los reinicios (en un sistema de archivos NTFS). – sqweek