10

当我打开一个文件时,我想知道它是否正在被另一个进程使用,以便我可以进行特殊处理;任何其他 IOException 我都会冒泡。IOException 的 Message 属性包含“进程无法访问文件 'foo',因为它正被另一个进程使用。”,但这不适合编程检测。检测另一个进程正在使用的文件的最安全、最可靠的方法是什么?

4

2 回答 2

16

IOException当从 Win32 本机函数返回的错误代码为ERROR_SHARING_VIOLATION( Documentation)时,将抛出此特定版本。它的数值为 ,0x20但实际上存储为异常0x80070020HRESULT属性(这是调用 MakeHRFromErrorCode 的结果)。

因此,检查共享冲突的程序化方法是检查valueHResult上的属性。 IOException0x80070020

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

但是,我确实质疑在由于共享违规而引发的情况下您到底想做什么。抛出异常的那一刻,其他进程可能会退出,从而消除违规。

于 2010-04-02T20:09:32.790 回答
1

我没有足够的“代表”来发表评论,所以希望这个“答案”还可以……

接受的答案正是我一直在寻找的并且完美地工作,但是这里的人和类似问题的人质疑检查文件是否被锁定的实用程序。测试文件是否被锁定的实用程序函数没有多大用处是正确的,因为在下一条语句中状态可能已经改变。

但是尝试锁定操作然后以不同方式响应锁定错误与一般错误的模式是有效且有用的。最明显的做法是稍等片刻,然后重试操作。这可以概括为一个辅助函数,如下所示:

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

...无论如何,只是发布以防有类似需求的人发现该模式有用。

于 2015-11-04T20:25:55.260 回答