2013-05-05 175 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