2009-07-08 72 views
1

我正在做一個日誌歸檔服務。由於某些文件已打開並正在使用中,因此我們無法將它們存檔直到它們不再被使用。我可以找出如何查看文件是否被另一個進程打開的唯一方法是下面的代碼。有沒有更好的方法來確定文件是否被.NET中的另一個應用程序打開?

討厭代碼裏面一定有更好的辦法。有沒有更好的辦法?

try 
{ 
    using (FileStream stream = File.Open("SomeFile.txt", FileMode.Open, FileAccess.ReadWrite)) 
    { 
     // Do nothing just seeing if I could open it. 
     return true; 
    } 
} 
catch (IOException) 
{ 
    // Exception so File is in use and can't open 
    return false; 
} 

編輯:請注意,我不想捕獲異常。我寧願完全避免這個例外。我寧願有一些神奇的功能,例如IsFileInUser(字符串文件名),它會返回一個布爾值,但沒有捕獲下面的異常。

我看到的一種方法是使用OpenFile of PInvoke

+0

即使你有一個魔術函數返回一個布爾值 - 就像zvolkov指出的那樣,當你嘗試基於布爾值操作時,仍然可能會遇到異常。我相信最好的辦法是自己鎖定文件,鎖定作者 - 正如我的回答中所述。 – 2009-07-08 19:03:34

回答

6

這是我想出來的。

首先,這是一個例外的壞方法。

public static bool IsFileInUseBadMethod(string filePath) 
{ 
    if (!File.Exists(filePath)) 
    { 
     return false; 
    } 

    try 
    { 
     using (StreamReader reader = new StreamReader(File.Open(filePath, FileMode.Open, FileAccess.ReadWrite))) 
     { 
      return false; 
     } 
    } 
    catch (IOException) 
    { 
     return true; 
    } 
} 

在我的機器上這個方法在文件被使用時花費了大約一千萬個滴答。

另一種方式是使用kernel32。dll的

// Need to declare the function from the kernel32 
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 
private static extern Microsoft.Win32.SafeHandles.SafeFileHandle CreateFile(string lpFileName, System.UInt32 dwDesiredAccess, System.UInt32 dwShareMode, IntPtr pSecurityAttributes, System.UInt32 dwCreationDisposition, System.UInt32 dwFlagsAndAttributes, IntPtr hTemplateFile); 

private static readonly uint GENERIC_WRITE = 0x40000000; 
private static readonly uint OPEN_EXISTING = 3; 

下面是函數

public static bool IsFileInUse(string filePath) 
{ 
    if (!File.Exists(filePath)) 
    { 
     return false; 
    } 

    SafeHandle handleValue = null; 

    try 
    { 
     handleValue = FileHelper.CreateFile(filePath, FileHelper.GENERIC_WRITE, 0, IntPtr.Zero, FileHelper.OPEN_EXISTING, 0, IntPtr.Zero); 

     bool inUse = handleValue.IsInvalid; 

     return inUse; 
    } 
    finally 
    { 
     if (handleValue != null) 
     { 
      handleValue.Close(); 

      handleValue.Dispose(); 

      handleValue = null; 
     } 
    } 
} 

這跑了約180萬蜱。 來自異常方法的超過800萬個滴答差異

但是,當文件沒有被使用時意味着不會拋出異常,「BAD」方法的速度是130萬,kernel32.dll是200萬。大約800k蜱更好。因此,如果我知道該文件大約1/10時間未被使用,那麼使用Exception方法會更好。然而,它是一次,與kernel32.dll相比,它是一個非常昂貴的ticks操作。

1

這個問題已經出現在SO之前。搜索文件鎖定/文件鎖定。總結答案:不,沒有更好的可靠的方式。

理論上,您可以使用Windows API來查找該文件的打開IO句柄,但即使您看到該文件未被鎖定,然後繼續進行邏輯處理,也可能在嘗試之前再次鎖定該文件訪問它:

if FileIsNotLocked(file) { 
    ...       <=== file can get re-locked here! 
    do something 
} 
3

我會在你的代碼示例中改進的唯一方法是捕獲將由File.Open引發的一些特定的異常。您至少需要捕獲IOException和UnauthorizedAccessException。

通過放入catch,你可以捕獲任何可能拋出的異常,這是不好的做法。

+0

我明白這一點,但是爲了簡潔起見,我只是放了一下。 – 2009-07-08 18:04:17

1

要避免zvolkov提到的問題,如果您不介意在進行歸檔時鎖定其他應用程序,則可以嘗試使用拒絕作者而不是讀者的鎖來打開文件。然後繼續並歸檔該文件。當然,這將鎖定任何試圖打開文件進行更新的應用程序 - 但鑑於您希望在某個時刻對文件進行存檔,沒有什麼好辦法擺脫困境。

using (FileStream stream = new FileStream(logFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) 
{ 
    // Archive your log. 
} 
0

我認爲你只是要抓住例外。我只是將閱讀和測試結合成一個,也許就像這樣。

bool success = false; 

while(!success) 
{ 
    try 
    { 
    //TODO: Try to open the file and write to it 
    success = true; 
    } 
    catch(IOException ex) 
    { 
    // File in use, wait a little bit and try again. 
    Thread.Sleep(xxx); 
    } 
} 
相關問題