UnauthorizedAccessException on creating file after deletion using FileStream

孤街醉人 提交于 2019-12-12 09:24:53

问题


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:

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:

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?


回答1:


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.

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.

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.

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.




回答2:


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.

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


来源:https://stackoverflow.com/questions/28405934/unauthorizedaccessexception-on-creating-file-after-deletion-using-filestream

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!