2013-05-05 136 views
5

我试图追踪文件和文件夹的整个生命周期(它们可能会被移动或重命名)。我已经完成搜索,发现FileSystemWatcher可能是最流行的跟踪文件和文件夹的方式,但是,这对我来说并不适用,因为应用程序可能会或可能不会一直运行。我选择尝试通过ID跟踪文件夹。获取文件夹ID C#

我找到了一种方法来跟踪来自this stack post答案的ID中的文件。我可以根据this answer中的方法B成功获取文件ID。

在搜索时,我发现this stack post表示他使用FSCTL_GET_OBJECT_ID找到了他的解决方案。我花了不少时间试图弄清楚如何使用这个功能,但是我无法把头围住它。我基本上没有从C#中调用本机窗口函数的经验。

任何人都可以给我一个在这个正确的方向推?我觉得我必须失去一些明显的东西。

是否有原因C#无法访问文件/文件夹ID?跟踪文件/文件夹不常见?

编辑,添加代码:

 static uint returnVal; 

    //Working example to get File ID 
    public static string GetFileId(string path) 
    { 
     WinAPI.BY_HANDLE_FILE_INFORMATION objectFileInfo = new WinAPI.BY_HANDLE_FILE_INFORMATION(); 

     FileInfo fi = new FileInfo(path); 
     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.ToString(); 
    } 

    public static string GetFolderId(string path) 
    { 
     //Get a handle on the given folder 
     IntPtr cFile = WinAPI.CreateFile(
      path, 
      WinAPI.GENERIC_READ, 
      FileShare.Read, 
      IntPtr.Zero, 
      (FileMode)WinAPI.OPEN_EXISTING, 
      WinAPI.FILE_FLAG_BACKUP_SEMANTICS, 
      IntPtr.Zero); 

     Console.WriteLine(path); 
     Console.WriteLine(cFile); 

     if ((int)cFile != -1) 
     { 
      int cFileSize = Marshal.SizeOf(typeof(IntPtr)); 
      Console.WriteLine("cFile size = {0}", cFileSize); 

      IntPtr cFileBlob = Marshal.AllocHGlobal(cFileSize); 
      uint numBytesRead = 0; 

      WinAPI.DeviceIoControl(cFile, WinAPI.FSCTL_GET_OBJECT_ID, IntPtr.Zero, 0, cFileBlob, (uint)cFileSize, ref numBytesRead, IntPtr.Zero); 

      if (returnVal == 0) 
      { 
       Console.WriteLine(Marshal.GetLastWin32Error()); // Returning error 87 here 
      } 
     } 

     //Should be returning the ID from the folder. 
     return String.Empty; 
    } 

    public static void Main(string[] args) 
    { 
    Console.WriteLine(GetFileId(@"C:\Users\Matt\Desktop\TestDocument.txt")); 
    Console.WriteLine(GetFolderId(@"C:\Users\Matt\Desktop")); 
    } 

} 

class WinAPI 
{ 
    // Win32 constants for accessing files. 
    internal const int GENERIC_READ = unchecked((int)0x80000000); 
    internal const int FILE_FLAG_BACKUP_SEMANTICS = unchecked((int)0x02000000); 
    internal const int OPEN_EXISTING = unchecked((int)3); 
    internal const int FSCTL_GET_OBJECT_ID = 0x0009009c; 
    internal const int FSCTL_CREATE_OR_GET_OBJECT_ID = 0x000900c0; 

    [DllImport("kernel32.dll", SetLastError = true)] 
    public static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode, IntPtr lpInBuffer, uint nInBufferSize, [Out] IntPtr lpOutBuffer, uint nOutBufferSize, ref uint lpBytesReturned, IntPtr lpOverlapped); 

    [DllImport("kernel32.dll", SetLastError = true)] 
    public static extern bool GetFileInformationByHandle(IntPtr hFile, out BY_HANDLE_FILE_INFORMATION lpFileInformation); 

    [DllImport("kernel32.dll", SetLastError = true)] 
    public static extern IntPtr CreateFile(
     String fileName, 
     int dwDesiredAccess, 
     System.IO.FileShare dwShareMode, 
     IntPtr securityAttrs_MustBeZero, 
     System.IO.FileMode dwCreationDisposition, 
     int dwFlagsAndAttributes, 
     IntPtr hTemplateFile_MustBeZero); 

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

我从DeviceIoControl的,这是根据MSDN上(我无法发布由于声誉的限制更多的链接后无效参数得到错误“87”后)

+0

http://msdn.microsoft.com/en-us/library/windows/desktop/bb776914%28v=vs.85%29.aspx – MethodMan 2013-05-06 00:12:07

+0

这是高档功能服务器,具有自动故障转移功能的群集。它不会在您的开发机器的C:驱动器上工作。 – 2013-05-06 02:06:06

+0

@HansPassant,请您详细说明一下吗?它在他所链接的信息源中工作,并且在我看到的API描述中没有任何信息表明它不能在桌面上工作。 – 2013-05-06 02:13:57

回答

3

似乎你没有问题,FileSystemWatcher。所以,如何在C#中使用DeviceIoControl,看看这个答案:

class Program { 
    const uint FSCTL_GET_OBJECT_ID=0x0009009c; 

    public static String GetFileId(String path) { 
     using(var fs=File.Open(
      path, 
      FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite) 
      ) { 
      WinAPI.BY_HANDLE_FILE_INFORMATION info; 
      WinAPI.GetFileInformationByHandle(fs.Handle, out info); 
      return String.Format(
        "{0:x}", ((info.FileIndexHigh<<32)|info.FileIndexLow)); 
     } 
    } 

    public static WinAPI.FILE_OBJECTID_BUFFER GetFolderIdBuffer(String path) { 
     using(var hFile=WinAPI.CreateFile(
      path, 
      WinAPI.GENERIC_READ, FileShare.Read, 
      IntPtr.Zero, 
      (FileMode)WinAPI.OPEN_EXISTING, 
      WinAPI.FILE_FLAG_BACKUP_SEMANTICS, 
      IntPtr.Zero 
      )) { 
      if(null==hFile||hFile.IsInvalid) 
       throw new Win32Exception(Marshal.GetLastWin32Error()); 

      var buffer=default(WinAPI.FILE_OBJECTID_BUFFER); 
      var nOutBufferSize=Marshal.SizeOf(buffer); 
      var lpOutBuffer=Marshal.AllocHGlobal(nOutBufferSize); 
      var lpBytesReturned=default(uint); 

      var result= 
       WinAPI.DeviceIoControl(
        hFile, FSCTL_GET_OBJECT_ID, 
        IntPtr.Zero, 0, 
        lpOutBuffer, nOutBufferSize, 
        ref lpBytesReturned, IntPtr.Zero 
        ); 

      if(!result) 
       throw new Win32Exception(Marshal.GetLastWin32Error()); 

      var type=typeof(WinAPI.FILE_OBJECTID_BUFFER); 

      buffer=(WinAPI.FILE_OBJECTID_BUFFER) 
       Marshal.PtrToStructure(lpOutBuffer, type); 

      Marshal.FreeHGlobal(lpOutBuffer); 
      return buffer; 
     } 
    } 

    public static void Main(String[] args) { 
     Console.WriteLine(
      GetFileId(@"C:\Users\Matt\Desktop\TestDocument.txt")); 

     var buffer=GetFolderIdBuffer(@"C:\Users\Matt\Desktop"); 

     var objectId=buffer.ObjectId 
       .Reverse() 
       .Select(x => x.ToString("x2")) 
       .Aggregate(String.Concat); 

     Console.WriteLine("{0}", objectId); 
    } 
} 

class WinAPI { 
    internal const int 
     GENERIC_READ=unchecked((int)0x80000000), 
     FILE_FLAG_BACKUP_SEMANTICS=unchecked((int)0x02000000), 
     OPEN_EXISTING=unchecked((int)3); 

    [StructLayout(LayoutKind.Sequential)] 
    public struct FILE_OBJECTID_BUFFER { 
     public struct Union { 
      [MarshalAs(UnmanagedType.ByValArray, SizeConst=16)] 
      public byte[] BirthVolumeId; 

      [MarshalAs(UnmanagedType.ByValArray, SizeConst=16)] 
      public byte[] BirthObjectId; 

      [MarshalAs(UnmanagedType.ByValArray, SizeConst=16)] 
      public byte[] DomainId; 
     } 

     [MarshalAs(UnmanagedType.ByValArray, SizeConst=16)] 
     public byte[] ObjectId; 

     public Union BirthInfo; 

     [MarshalAs(UnmanagedType.ByValArray, SizeConst=48)] 
     public byte[] ExtendedInfo; 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    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; 
    } 

    [DllImport("kernel32.dll", SetLastError=true)] 
    public static extern bool DeviceIoControl(
     SafeFileHandle hDevice, 
     uint dwIoControlCode, 
     IntPtr lpInBuffer, 
     uint nInBufferSize, 
     [Out] IntPtr lpOutBuffer, 
     int nOutBufferSize, 
     ref uint lpBytesReturned, 
     IntPtr lpOverlapped 
     ); 

    [DllImport("kernel32.dll", SetLastError=true)] 
    public static extern SafeFileHandle CreateFile(
     String fileName, 
     int dwDesiredAccess, 
     System.IO.FileShare dwShareMode, 
     IntPtr securityAttrs_MustBeZero, 
     System.IO.FileMode dwCreationDisposition, 
     int dwFlagsAndAttributes, 
     IntPtr hTemplateFile_MustBeZero 
     ); 

    [DllImport("kernel32.dll", SetLastError=true)] 
    public static extern bool GetFileInformationByHandle(
     IntPtr hFile, out BY_HANDLE_FILE_INFORMATION lpFileInformation); 
} 

名称空间是必需的:

Physical disk size not correct (IoCtlDiskGetDriveGeometry)

针对这个问题,它与下面的代码完成使用:

using Microsoft.Win32.SafeHandles; 
+0

添加了我目前为止的代码。我能够为文件检索I,但是我调用DeviceIoControl时完全空白。 – Matt 2013-05-05 22:18:45

+0

@Matt:我正在阅读.. – 2013-05-05 23:46:24

+0

感谢您花时间阅读它。我已经更新了一些进展。 – Matt 2013-05-06 01:52:47