Когда я открываю файл, я хочу знать, используется ли он другим процессом, поэтому я могу выполнять специальную обработку; любое другое исключение IO. Свойство Message IOException содержит "Процесс не может получить доступ к файлу" foo ", потому что он используется другим процессом.", Но это не подходит для программного обнаружения. Что является самым безопасным, наиболее надежным способом обнаружения файла, используемого другим процессом?
Как узнать, связано ли обнаруженное IOException с файлом, используемым другим процессом, не прибегая к анализу свойства Message
Ответ 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;
}
... в любом случае, просто публиковать в случае, если кто-то с подобными потребностями находит шаблон полезным.