2016-06-11 63 views
2

我有一個Windows Forms應用程序,它使用File.Move來響應用戶請求移動文件。這不是一個異步方法,但是當目標位於同一個驅動器上時,它幾乎是立即發生的(我假設它只是重寫頭並不移動任何東西)。但是,如果在同一驅動器上的目標是而不是,則會阻止應用程序。不可取。確定文件移動的目標是否位於C#中的同一文件系統上

所以很自然地想到了切換到異步。它看起來像這樣做的方式是使用文件流並在刪除原始文件之前使用Stream.CopyToAsync。這很好,除非它在同一個驅動器上的,這是很多浪費的複製。

在決定執行哪個操作之前,是否有可靠的方法來確定文件的建議目標位於與當前存儲的驅動器不同的驅動器上?我顯然可以看看驅動器盤符,但這看起來有些不禮貌,我認爲有些情況下,如連接點無法正常工作。

另一種可能的選擇是使用類似Task.Run(() => File.Move(x))的東西,儘管我對此有一種模糊的感覺。

+0

驅動器號不一定會工作(我認爲),條紋卷或RAID呢? - 試圖想出想法,但是想到這個想法 – jdphenix

+0

@jdphenix是的,好的,除了交接點外,這是另一個原因,不要這樣做。 – Casey

+0

Windows或* nix?或者需要一些便攜式? – jdphenix

回答

1

我認爲最好的努力是比較音量序列號。 GetFileInformationByHandle()能夠獲得文件句柄的卷序號。這絕對是RAID /條帶卷的YMMV,所以如果這些適用,我會測試此解決方案的性能。它確實工作的交界點。

的的DllImport它是

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

我們還需要一種方式來獲得適當的文件句柄來使用。由於我們正在處理潛在目標,如果我們假設目標目錄已經存在,我們可以使用CreateFile()來獲取目錄句柄。

CreateFile(),則是的DllImport

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
private static extern IntPtr CreateFile(
    [MarshalAs(UnmanagedType.LPTStr)] string filename, 
    [MarshalAs(UnmanagedType.U4)] FileAccess access, 
    [MarshalAs(UnmanagedType.U4)] FileShare share, 
    IntPtr securityAttributes, 
    [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, 
    [MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes, 
    IntPtr templateFile); 

我們還需要CloseHandle()

[DllImport("kernel32.dll", SetLastError = true)] 
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
[SuppressUnmanagedCodeSecurity] 
[return: MarshalAs(UnmanagedType.Bool)] 
private static extern bool CloseHandle(IntPtr hObject); 

有了所有這些片段,創建一個返回true的包裝方法,如果卷序列號提供的路徑匹配。這顯然是有道理的拋出更具體的異常,但爲了簡潔起見:

static bool SameVolume(string src, string dest) 
{ 
    IntPtr srcHandle = IntPtr.Zero, destHandle = IntPtr.Zero; 
    try 
    { 
     srcHandle = CreateFile(src, FileAccess.Read, FileShare.ReadWrite, IntPtr.Zero, 
      FileMode.Open, (FileAttributes) 0x02000000, IntPtr.Zero); 
     destHandle = CreateFile(dest, FileAccess.Read, FileShare.ReadWrite, IntPtr.Zero, 
      FileMode.Open, (FileAttributes) 0x02000000, IntPtr.Zero); 
     var srcInfo = new BY_HANDLE_FILE_INFORMATION(); 
     var destInfo = new BY_HANDLE_FILE_INFORMATION(); 

     if (!GetFileInformationByHandle(srcHandle, out srcInfo)) 
     { 
      throw new Exception(Marshal.GetLastWin32Error().ToString()); 
     } 

     if (!GetFileInformationByHandle(destHandle, out destInfo)) 
     { 
      throw new Exception(Marshal.GetLastWin32Error().ToString()); 
     } 

     return srcInfo.VolumeSerialNumber == destInfo.VolumeSerialNumber; 
    } 
    finally 
    { 
     if (srcHandle != IntPtr.Zero) 
     { 
      CloseHandle(srcHandle); 
     } 

     if (destHandle != IntPtr.Zero) 
     { 
      CloseHandle(destHandle); 
     } 
    } 
} 

或者,你可以只使用

Task.Run(() => File.Move(x)) 

或另一個異步結構得到這個無鎖起來做應用程序的界面。我看不出有什麼問題。

+0

我記得有很多東西說不要使用Task.Run進行I/O綁定的工作,但重新審視它似乎理由與我無關,所以你可能是正確的追求簡單的過程。無論如何,這似乎回答了一切。 – Casey

相關問題