【问题标题】:How do I determine if an IOException is thrown because of a sharing violation?如何确定是否因共享冲突而引发 IOException?
【发布时间】:2010-09-30 08:53:03
【问题描述】:

我有一个 C# 应用程序,我想将一个文件复制到一个新位置。有时我需要覆盖现有文件。 发生这种情况时,我会收到 System.IO.IOException。我想从共享冲突中恢复,但是如何确定返回 IOException 是因为目标文件正在使用而不是其他原因?我可以查找“该进程无法访问该文件,因为它正在被另一个进程使用”。消息...但我不喜欢这个主意。

【问题讨论】:

    标签: c# vb.net .net-3.5


    【解决方案1】:

    这是我想出的解决方案。

    private void RobustMoveFile( System.IO.DirectoryInfo destinationDirectory, System.IO.FileInfo sourceFile, Boolean retryMove )
                    {
                        try
                        {
                            string DestinationFile = Path.Combine( destinationDirectory.FullName, sourceFile.Name );
                            if ( File.Exists( DestinationFile ) )
                                sourceFile.Replace( DestinationFile, DestinationFile + "Back", true );
                            else
                            {
                                sourceFile.CopyTo( DestinationFile, true );
                                sourceFile.Delete();
                            }
                        }
                        catch ( System.IO.IOException IOEx )
                        {
                            int HResult = System.Runtime.InteropServices.Marshal.GetHRForException( IOEx );        
                            const int SharingViolation = 32;
                            if ( ( HResult & 0xFFFF ) == SharingViolation && retryMove )
                                RobustMoveFile( destinationDirectory, sourceFile, false );
                            throw;
                        }
                    }
    

    【讨论】:

    • 不需要检查目标文件是否存在,因为其他一些进程可能会在检查和替换/复制操作之间创建/删除它。只需更改您始终创建目标文件的逻辑,如果失败,请恢复或重试。
    【解决方案2】:

    正如其他答案所述,您需要获取错误的 HResult 并进行检查。 32 的 HResult 是共享冲突。

    .NET 4.5 中,IOException 有一个公共的HResult 属性,因此您可以执行以下操作:

    try
    {
        // do file IO here
    }
    catch (IOException e)
    {
        if (e.HResult == 32) // 32 = Sharing violation
        {
            // Recovery logic goes here
        }
        else
        {
            throw; // didn't need to catch this
        }
    }
    

    但是,在早期版本的 .NET 中,您需要通过调用 Marshal.GetHRForException(Exception) 来获取 HResult,因此类似的代码将是:

    try
    {
        // do file IO here
    }
    catch (IOException e)
    {
        int HResult = System.Runtime.InteropServices.Marshal.GetHRForException(e)
        if (HResult == 32) // 32 = Sharing violation
        {
            // Recovery logic goes here
        }
        else
        {
            throw; // Or do whatever else here
        }
    }
    

    C# 6.0 允许您使用此语法仅捕获带有 when 子句的共享冲突:

    try
    {
        // do file IO here
    }
    catch (IOException e) when (e.HResult == 32) // 32 = Sharing violation
    {
        // Recovery logic goes here
    }
    

    【讨论】:

    • 你确定这完全正确吗?对此进行试验,我认为您需要按位和 (&&) 使用 0x000000000000FFFF 的 HResult。所以你要检查 (0xFFFF && e.HResult)==32
    • @Greylander 我认为您对按位执行的要求是正确的,但运算符是“&”,而不是“&&”。后者是逻辑短路“和”。 BTW here is a reference 用于将 W32 错误转换为 HRESULT。所以按位-and 的目的是反其道而行之。
    【解决方案3】:

    寻找您可以处理的显式错误代码,例如:

    catch (例外 u) { if (((SocketException)u).ErrorCode == 10035) ...

    看这里: http://msdn.microsoft.com/en-us/library/ms681391(VS.85).aspx

    对于错误代码,例如:

    ERROR_SHARING_VIOLATION - 32 - 0x20

    ERROR_ACCESS_DENIED = 5 - 0x5

    ERROR_FILE_NOT_FOUND - 2 - 0x2

    【讨论】:

    • 从 IOException 到 SocketException 的转换将如何成功?
    • 只是一个例子......没想到它会让任何人失望
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-02-02
    • 1970-01-01
    • 2022-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-23
    相关资源
    最近更新 更多