Как определить, вызвано ли перехваченное IOException файлом, который используется другим процессом, не прибегая к анализу свойства Message исключения - PullRequest
10 голосов
/ 02 апреля 2010

Когда я открываю файл, я хочу знать, используется ли он другим процессом, чтобы я мог выполнить специальную обработку; Любое другое IOException я буду пузыриться. Свойство Message IOException содержит «Процесс не может получить доступ к файлу« foo », потому что он используется другим процессом.», Но это не подходит для программного обнаружения. Какой самый безопасный и надежный способ обнаружить файл, используемый другим процессом?

Ответы [ 2 ]

13 голосов
/ 03 апреля 2010

Эта конкретная версия IOException генерируется, когда код ошибки, возвращенный из встроенной функции Win32, равен ERROR_SHARING_VIOLATION ( Документация) . Он имеет числовое значение 0x20, но на самом деле хранится как 0x80070020 в свойстве HRESULT исключения (это результат вызова MakeHRFromErrorCode).

Таким образом, программный способ проверки нарушения совместного доступа заключается в проверке свойства HResult в IOException на значение 0x80070020.

public static bool IsSharingViolation(this IOException ex) {
  return 0x80070020 == Marshal.GetHRForException(ex);
}

Однако я действительно задаю вопрос, что именно вы хотите сделать в сценарии, который был брошен в результате нарушения совместного доступа. В момент возникновения исключения другой процесс может выйти и, следовательно, устранить нарушение.

1 голос
/ 04 ноября 2015

Мне не хватает «представителя», чтобы комментировать, так что, надеюсь, этот «ответ» в порядке ...

Принятый ответ - это именно то, что я искал, и он отлично работает, но люди здесь и по аналогичным вопросам подвергли сомнению полезность проверки, заблокирован ли файл. Правильно, что служебная функция для проверки, заблокирован ли файл, не очень полезна, потому что при следующем утверждении статус мог измениться.

Но шаблон попытки выполнить операцию блокировки и затем по-разному реагировать на ошибки блокировки по сравнению с общими ошибками является допустимым и полезным. Самое очевидное, что нужно сделать, это немного подождать и повторить операцию. Это можно обобщить в вспомогательную функцию, например:

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;
}

... в любом случае, просто отправка сообщений на случай, если кто-то с похожими потребностями сочтет эту модель полезной.

...