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

时间:2022-03-15 10:05:23

I am facing an issue on client system. On trying to reproduce it in a sample code I have reproduced it.

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

Here's the sample code

这是示例代码

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

Just Run this code and click the button 3-4 times and notice the exception as shown in screenshot below:

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

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

The stacktrace of this exception is:

此异常的堆栈跟踪是:

System.UnauthorizedAccessException was unhandled Message=Access to the path 'D:\SomeFile.txt' is denied. Source=mscorlib StackTrace: at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath) at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy) at System.IO.FileStream..ctor(String path, FileMode mode) at WindowsApplication1.Form1.createFile() in C:\Users\premjeet.singh\Desktop\WindowsApplication1\WindowsApplication1\Form1.vb:line 23 at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.runTryCode(Object userData) at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart() InnerException:

System.UnauthorizedAccessException未处理Message =拒绝访问路径'D:\ SomeFile.txt'。 Source = mscorlib StackTrace:at System.IO .__ Error.WinIOError(Int32 errorCode,String maybeFullPath)at 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(String path,FileMode mode,FileAccess access,FileShare share,Int32 bufferSize,FileOptions options,String msgPath,Boolean bFromProxy)at at位于System.Threading的C:\ Users \ premjeet.singh \ Desktop \ WindowsApplication1 \ WindowsApplication1 \ Form1.vb:第23行中的WindowsApplication1.Form1.createFile()处的System.IO.FileStream..ctor(字符串路径,FileMode模式)。 System.Threadi上System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode代码,CleanupCode backoutCode,Object userData)的System.Threading.ExecutionContext.runTryCode(Object userData)中的ThreadHelper.ThreadStart_Context(Object state) System.Threading.ExecutionContext.Run(ExecutionContext executionContext,ContextCallback回调)的System.Threading.ExecutionContext.Run(ExecutionContext executionContext,ContextCallback回调,Object状态,Boolean ignoreSyncCtx)中的ng.ExecutionContext.RunInternal(ExecutionContext executionContext,ContextCallback回调,Object状态) ,对象状态)在System.Threading.ThreadHelper.ThreadStart()InnerException:

Can anybody let me know the reason for this UnauthorizedAccessException exception as the file is already deleted before creating the new one and how it can be solved?

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

2 个解决方案

#1


8  

This is pretty normal, you are doing battle with other processes that run on your machine that are also interested in the file. Better known as "anti-malware" and "search indexer". The "click 3 times" scenario is just the failure-mode you induce by those other processes taking a while to have a look-see at the file content.

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

Such processes will open the file for delete sharing to minimize their impact. Same capability is exposed in .NET, FileShare.Delete option. That works, to a degree, you don't have any trouble deleting the file as you found out. But the file will not actually disappear from the file system until those other processes close their handle on the file. While it still exists, any process that tries to open or overwrite the pending-delete file will be slapped with an "access denied" error.

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

In general, you never want to use the approach you use now, deleting the file and then trying to recreate it. Given the considerable odds that this can fail, you'll leave the user with no file at all. That's irrecoverable loss of data. The alternative is to swap the file, supported by the File.Replace() method. Which works well for locked files, these processes only have a lock on the file data, not on the directory entry. In other words, you can rename the file without trouble.

通常,您永远不想使用现在使用的方法,删除文件然后尝试重新创建它。鉴于这可能失败的可能性很大,你将完全没有文件。这是不可恢复的数据丢失。另一种方法是交换文件,由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

This still isn't perfect but the odds for data loss are eliminated and you give the process that is holding on the file more time to finish using it. Whether you want to put a retry-loop around the swap operation is up to you.

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

Technically it is possible to make it perfect, you have to give the backup filename a random name so you can never fail to delete it the next time you create the file. That however sprays files to the disk that are hard to get rid of. If you do this then you must also move the file to the drive's Recycle Bin so it will get deleted some day in the future. Luckily that's easy to do in VB.NET, use the DeleteFile() helper function and specify RecycleOption.SendToRecycleBin. But only appropriate if you save to a local drive.

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

#2


-4  

When you invoke File.Delete(path), you must give the CPU some time to finish deleting the file before coming back to execute any further code that might have anything to do with the same file name as the one just deleted.

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

Several ways can accomplish the same thing. Here is how I would do it:

有几种方法可以完成同样的事情。我是这样做的:

...
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
....

#1


8  

This is pretty normal, you are doing battle with other processes that run on your machine that are also interested in the file. Better known as "anti-malware" and "search indexer". The "click 3 times" scenario is just the failure-mode you induce by those other processes taking a while to have a look-see at the file content.

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

Such processes will open the file for delete sharing to minimize their impact. Same capability is exposed in .NET, FileShare.Delete option. That works, to a degree, you don't have any trouble deleting the file as you found out. But the file will not actually disappear from the file system until those other processes close their handle on the file. While it still exists, any process that tries to open or overwrite the pending-delete file will be slapped with an "access denied" error.

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

In general, you never want to use the approach you use now, deleting the file and then trying to recreate it. Given the considerable odds that this can fail, you'll leave the user with no file at all. That's irrecoverable loss of data. The alternative is to swap the file, supported by the File.Replace() method. Which works well for locked files, these processes only have a lock on the file data, not on the directory entry. In other words, you can rename the file without trouble.

通常,您永远不想使用现在使用的方法,删除文件然后尝试重新创建它。鉴于这可能失败的可能性很大,你将完全没有文件。这是不可恢复的数据丢失。另一种方法是交换文件,由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

This still isn't perfect but the odds for data loss are eliminated and you give the process that is holding on the file more time to finish using it. Whether you want to put a retry-loop around the swap operation is up to you.

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

Technically it is possible to make it perfect, you have to give the backup filename a random name so you can never fail to delete it the next time you create the file. That however sprays files to the disk that are hard to get rid of. If you do this then you must also move the file to the drive's Recycle Bin so it will get deleted some day in the future. Luckily that's easy to do in VB.NET, use the DeleteFile() helper function and specify RecycleOption.SendToRecycleBin. But only appropriate if you save to a local drive.

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

#2


-4  

When you invoke File.Delete(path), you must give the CPU some time to finish deleting the file before coming back to execute any further code that might have anything to do with the same file name as the one just deleted.

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

Several ways can accomplish the same thing. Here is how I would do it:

有几种方法可以完成同样的事情。我是这样做的:

...
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
....