Как узнать, связано ли обнаруженное IOException с файлом, используемым другим процессом, не прибегая к анализу свойства Message
Когда я открываю файл, я хочу знать, используется ли он другим процессом, поэтому я могу выполнять специальную обработку; любое другое исключение IO. Свойство Message IOException содержит "Процесс не может получить доступ к файлу" foo ", потому что он используется другим процессом.", Но это не подходит для программного обнаружения. Что является самым безопасным, наиболее надежным способом обнаружения файла, используемого другим процессом?
Ответы
Ответ 1
Эта конкретная версия IOException
вызывается, когда код ошибки, возвращаемый из встроенной функции Win32, ERROR_SHARING_VIOLATION
(Документация). Он имеет числовое значение 0x20
, но фактически хранится как 0x80070020
в свойстве HRESULT
исключения (это результат вызова MakeHRFromErrorCode).
Таким образом, программный способ проверки нарушения совместного доступа проверяет свойство HRESULT
на IOException
для значения 0x80070020
.
public static bool IsSharingViolation(this IOException ex) {
return 0x80070020 == Marshal.GetHRForException(ex);
}
Однако я задаюсь вопросом, что именно вы хотите сделать в сценарии, который он выбрал в результате нарушения доступа. В тот момент, когда исключение выбрасывается, другой процесс может выйти и, следовательно, удалить нарушение.
Ответ 2
Мне не хватает "репутации" для комментариев, так что, надеюсь, этот "ответ" в порядке...
Принятый ответ - именно то, что я искал и работает отлично, но люди здесь и на подобных вопросах задали вопрос об утилите проверки того, заблокирован ли файл. Правильно, что функция утилиты для проверки, если файл заблокирован, не очень удобен, потому что в самом следующем утверждении состояние могло измениться.
Но шаблон попытки операции блокировки, а затем по-разному реагирует на ошибки блокировки и общие ошибки, является допустимым и полезным. Самое очевидное, что нужно подождать немного и повторить операцию. Это можно обобщить на вспомогательную функцию, подобную этой:
protected static void RetryLock(Action work) {
// Retry LOCK_MAX_RETRIES times if file is locked by another process
for (int i = 1; i <= LOCK_MAX_RETRIES; i++) {
try {
work();
return;
} catch (IOException ex) {
if (i == LOCK_MAX_RETRIES || (uint) ex.HResult != 0x80070020) {
throw;
} else {
// Min should be long enough to generally allow other process to finish
// while max should be short enough such that RETRIES * MAX isn't intolerable
Misc.SleepRandom(LOCK_MIN_SLEEP_MS, LOCK_MAX_SLEEP_MS);
}
}
}
} // RetryLock
..., который затем можно использовать следующим образом:
public string DoSomething() {
string strReturn = null;
string strPath = @"C:\Some\File\Path.txt";
// Do some initial work...
//----------------------------------------------------------------------------------------------------
// NESTED FUNCTION to do main logic, RetryLock will retry up to N times on lock failures
Action doWork = delegate {
using (FileStream objFile = File.Open(strPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)) {
// Does work here if lock succeeded, else File.Open will throw ex
strReturn = new StreamReader(objFile).ReadLine();
}
}; // delegate doWork
//----------------------------------------------------------------------------------------------------
RetryLock(doWork); // Throws original ex if non-locking related or tried max times
return strReturn;
}
... в любом случае, просто публиковать в случае, если кто-то с подобными потребностями находит шаблон полезным.