2009-12-08 36 views
36

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

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 informan sobre 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 utilizar 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.

¿Alguna idea?

Respuesta

17

Si llama al 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 secuencias de datos alternativas para almacenar los metadatos.

+0

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

+6

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

+0

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

28

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

Bueno, lo probé, pero encontré un problema: no funciona para archivos como Microsoft Office suite (doc, docx, xls ...) porque cada vez que ha realizado cambios, Office tiende a eliminar el archivo y crea un nuevo archivo para reemplazarlo, este resultado en el número de referencia cambió, aunque el número de referencia sigue siendo único. No funciona detectar cambios en esos archivos, y tal vez algunos otros programas tengan un enfoque similar también. Así que supongo que volveré a mi método CreationTime ... – VHanded

+0

FWIW, no pude conseguir el ApproachA de Ashley Henderson para trabajar, pero ApproachB funcionó. Pude confirmar la nota de VHanded que el ID de archivo cambia cuando modifica un documento de Word (docx), lo cual es una lástima porque los archivos de Office son los que quería rastrear. –

+0

"cada vez que hace un cambio, la Oficina tiende a eliminar el archivo" ... maldición, AutoCAD también lo hace. Volver a 'FileSystemWatcher' –

0

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

Asimismo, es necesaria una importación 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 
     ); 

voy a dar cuerpo a esta respuesta un poco más, después. 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#.

Cuestiones relacionadas