使用FileStream删除后创建文件时出现UnauthorizedAccessException

时间:2015-02-09 08:56:59

标签: .net vb.net multithreading filestream file-access

我在客户端系统上遇到问题。在尝试使用示例代码重现它时,我已经复制了它。

以下是示例代码

Imports System.IO

Public Class Form1

    Private _lock As New Object

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim t As New Threading.Thread(AddressOf createFile)
        With t
            .IsBackground = True
            .Name = Guid.NewGuid.ToString
            .Start()
        End With
    End Sub

    Private Sub createFile()
        Dim path As String = "D:\SomeFile.txt"

        For i As Integer = 0 To 1000
            SyncLock _lock
                If File.Exists(path) Then File.Delete(path)

                Using fs As New FileStream(path, FileMode.CreateNew)

                End Using
            End SyncLock    
        Next
    End Sub
End Class

只需运行此代码并单击按钮3-4次,并注意异常,如下面的屏幕截图所示:

enter image description here

此异常的堆栈跟踪是:

  

System.UnauthorizedAccessException未处理Message = Access to   路径'D:\ SomeFile.txt'被拒绝。 Source = mscorlib StackTrace:          在System.IO .__ Error.WinIOError(Int32 errorCode,String maybeFullPath)          在System.IO.FileStream.Init(String path,FileMode mode,FileAccess access,Int32 rights,Boolean useRights,FileShare share,   Int32 bufferSize,FileOptions选项,SECURITY_ATTRIBUTES secAttrs,   String msgPath,Boolean bFromProxy,Boolean useLongPath)          在System.IO.FileStream..ctor(字符串路径,FileMode模式,FileAccess访问,FileShare共享,Int32 bufferSize,FileOptions   options,String msgPath,Boolean bFromProxy)          在System.IO.FileStream..ctor(字符串路径,FileMode模式)          位于C:\ Users \ premjeet.singh \ Desktop \ WindowsApplication1 \ WindowsApplication1 \ Form1.vb中的WindowsApplication1.Form1.createFile():行   23          在System.Threading.ThreadHelper.ThreadStart_Context(对象状态)          在System.Threading.ExecutionContext.runTryCode(Object userData)          在System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode)   代码,CleanupCode backoutCode,Object userData)          在System.Threading.ExecutionContext.RunInternal(ExecutionContext   executionContext,ContextCallback回调,对象状态)          at System.Threading.ExecutionContext.Run(ExecutionContext executionContext,ContextCallback callback,Object state,Boolean   ignoreSyncCtx)          在System.Threading.ExecutionContext.Run(ExecutionContext executionContext,ContextCallback回调,对象状态)          在System.Threading.ThreadHelper.ThreadStart()InnerException:

有没有人让我知道这个UnauthorizedAccessException异常的原因,因为在创建新文件之前文件已被删除以及如何解决它?

2 个答案:

答案 0 :(得分:8)

这很正常,您正在与您的计算机上运行的其他进程进行战斗,这些进程也对该文件感兴趣。更好地称为“反恶意软件”和“搜索索引器”。 “单击3次”场景只是您通过其他过程引发的故障模式,需要一段时间来查看文件内容。

此类进程将打开文件以进行删除共享,以最大限度地减少其影响。在.NET FileShare.Delete option中公开了相同的功能。这在某种程度上是有效的,您在发现时删除文件没有任何问题。但是文件系统实际上不会从文件系统中消失,直到其他进程关闭文件的句柄。虽然它仍然存在,但任何试图打开或覆盖挂起删除文件的进程都会被“访问被拒绝”错误打包。

通常,您从不想要使用您现在使用的方法,删除文件然后尝试重新创建它。鉴于这可能失败的可能性很大,您将完全没有文件。这是不可恢复的数据丢失。另一种方法是交换文件,由File.Replace()方法支持。哪个适用于锁定文件,这些进程只锁定文件数据,而不是目录条目。换句话说,您可以毫无困难地重命名文件。

Private Function createFile(ByVal outpath As String) As Boolean
    Dim temp As String = Path.Combine(Path.GetDirectoryName(outpath), Guid.NewGuid.ToString())
    Dim bak As String = outpath + ".bak"
    '' Create the file first
    Using fs As New FileStream(temp, FileMode.CreateNew)
        ''...
    End Using
    '' Now try to swap it in place
    Try
        File.Delete(bak)
        File.Replace(temp, outpath, bak)
    Catch
        File.Delete(temp)
        Return False
    End Try
    '' That worked, don't need the backup anymore.  Failure to delete is not fatal
    Try
        File.Delete(bak)
    Catch
    End Try
    Return True
End Function

这仍然不完美,但数据丢失的可能性已经消除,您可以让文件上的进程有更多时间来完成使用它。是否要在交换操作周围进行重试循环取决于您。

从技术上讲,它可以使其完美,您必须为备份文件名提供一个随机名称,以便下次创建文件时永远不会删除它。然而,这会将文件喷射到难以摆脱的磁盘上。如果您这样做,那么您还必须将文件移动到驱动器的回收站,以便将来某些日删除。幸运的是,在VB.NET中很容易做到,使用DeleteFile()辅助函数并指定RecycleOption.SendToRecycleBin。但只有在保存到本地驱动器时才适用。

答案 1 :(得分:-4)

当您调用File.Delete(path)时,您必须给CPU一些时间来完成删除文件,然后再返回执行任何可能与刚删除的文件名相同的任何其他代码。< / p>

有几种方法可以完成同样的事情。我将如何做到这一点:

...
If File.Exists(path) Then
   File.Delete(path)

   Application.DoEvents()   'Force completing any pending events 
                            'immediately start deleting the file

   System.Threading.Thread.Sleep(1000)     'optional: wait 1 second

   While System.IO.File.Exists(path)   'In case the file is still in 
                                       ' the process of being deleted
                                       ' Wait here for it to finish
   End While


   Using fs As New FileStream(path, FileMode.CreateNew)    'Now create the file

End If
....