DirectoryInfo.Delete(True) Doesn't Delete When Folder Structure is Open in Windows Explorer
Asked Answered
S

4

8

Assuming I have a folder structure like:

C:\MyTemp
   - MySubFolder

If I try to delete this using:

Dim path As String = "C:\MyTemp"
Dim di As System.IO.DirectoryInfo
di = System.IO.Directory.CreateDirectory(path)
di.CreateSubdirectory("MySubFolder")
di.Delete(True)

This works fine, unless I have Windows Explorer open and I'm looking at the 'MySubFolder' directory. Then I get an IOException The directory is not empty. - clicking OK dismisses this and then the folder structure is not deleted.

Any thoughts on how I can get this to perform correctly (i.e. delete), even when running this code while having the folder struture open in Windows Explorer?

Scholl answered 5/11, 2010 at 0:29 Comment(3)
Note that this is standard behavior of the shell. You will get the same error message from rmdir /S. I guess the deletion basically fails because Explorer still has a handle to the subfolder open.Hiroshige
@0xA3 - It's not consistent. See my comment on the answer below. There are cases where I can delete a folder while looking at it in Windows Explorer and then Explorer just navs to the parent folder of the child that was deleted.Scholl
@ToddMain I know this is old but I will really appreciate if you can post the solution for this.Symphonia
M
2

Only way you could get this to "work" 100% consistently is by nuking explorer (bad idea) or nuking the handle (also bad idea)

My recommendation would be to just handle the failure gracefully as opposed to trying this.

Murton answered 5/11, 2010 at 0:54 Comment(0)
D
1

Check out this article. IOException can be generated from an open handle to the directory: This open handle can result from enumerating directories and files which is exactly what opening in explorer does. Sounds like the actual error message is generic.

Dishrag answered 5/11, 2010 at 0:35 Comment(1)
yeah, i had read that, but it states that it only applies to WinXP and earlier. secondly, assume under a MySubfolder I have another subfolder called "Temp" and a file in it called "mypic.jpg". If I'm looking at the "Temp" folder in Windows Explorer and delete it (and the jpg) using the code above, it does delete. It's been inconsistent and I'm not sure what to do about it.Scholl
O
1

The best you can do is catch the error and then use handle.exe to find out which process is using the file and ask the user to close the application with options to retry or cancel.

Ever wondered which program has a particular file or directory open? Now you can find out. Handle is a utility that displays information about open handles for any process in the system. You can use it to see the programs that have a file open, or to see the object types and names of all the handles of a program.

Some more info here:

How to monitor process' IO activity using C#?

Overstate answered 5/11, 2010 at 1:13 Comment(0)
S
0

I came up with the following DirectoryInfo extension method which wraps the native DirectoryInfo.Delete() method and attempts to "safely delete" the specified folder:

This method requires the following COM reference: Microsoft Internet Controls COM Reference:  Microsoft Internet Controls x


    '''' <summary>
    '''' Attempts to perform a "Safe delete" by searching for any Windows File Explorer instances attached to the extended DirectoryInfo Object 
    '''' and navigate those instances off the current DirectoryInfo path in an attempt to prevent a "Directory is not empty" IOException when 
    '''' calling DirectoryInfo.Delete(recursive).
    '''' </summary>
    '''' <param name="DirectoryInfo">The DirectoryInfo object being extended</param>
    '''' <param name="recursive">Optional:  true to delete this directory, its subdirectories, and all files; otherwise false</param>
    '''' <returns>A Boolean indicating whether the DirectoryInfo.Delete(recursive) operation completed without an Exception</returns>
    '''' <remarks>Authored by CMC 2013-05-06 12:04:25 PM</remarks>
    <System.Runtime.CompilerServices.Extension()> _
    Public Function TrySafeDelete(ByVal [DirectoryInfo] As DirectoryInfo, Optional ByVal recursive As Boolean = False, Optional ByVal retryCount As Integer = 0) As Boolean
        Const maxRetryCount As Integer = 10
        retryCount = If(retryCount < 0, 0, retryCount)
        Dim success As Boolean = True
        If ([DirectoryInfo] IsNot Nothing) Then
            [DirectoryInfo].Refresh()
            Dim msWinShellIExplorerWindowsLockingCurrentDirectory As Dictionary(Of SHDocVw.InternetExplorer, DirectoryInfo) = New Dictionary(Of SHDocVw.InternetExplorer, DirectoryInfo)
            If ([DirectoryInfo].Exists()) Then
                Try
                    Dim msWinShellIExplorerWindows As SHDocVw.ShellWindows = New SHDocVw.ShellWindows()
                    For Each msWinShellIExplorerWindow As SHDocVw.InternetExplorer In msWinShellIExplorerWindows
                        If (msWinShellIExplorerWindow.Name.Equals("windows explorer", StringComparison.OrdinalIgnoreCase)) Then
                            Dim locationValue As String = msWinShellIExplorerWindow.LocationURL()
                            If (locationValue.Length() > 0) Then
                                Dim locationURI As Uri = Nothing
                                If (Uri.TryCreate(locationValue, UriKind.RelativeOrAbsolute, locationURI)) Then
                                    Dim msWinShellDirectoryInfo As DirectoryInfo = New DirectoryInfo(locationURI.LocalPath())
                                    Dim isLockingCurrentDirectory As Boolean = msWinShellDirectoryInfo.FullName.ToLower().Contains([DirectoryInfo].FullName.ToLower())
                                    If (isLockingCurrentDirectory AndAlso Not msWinShellIExplorerWindowsLockingCurrentDirectory.ContainsKey(msWinShellIExplorerWindow)) Then msWinShellIExplorerWindowsLockingCurrentDirectory.Add(msWinShellIExplorerWindow, msWinShellDirectoryInfo)
                                End If
                            End If
                        End If
                    Next

                    Dim navigateCompleteCount As Integer = 0
                    If (msWinShellIExplorerWindowsLockingCurrentDirectory.Any()) Then
                        For Each msWinShellDirectoryEntry As KeyValuePair(Of SHDocVw.InternetExplorer, DirectoryInfo) In msWinShellIExplorerWindowsLockingCurrentDirectory
                            Dim msWinShellIExplorerWindow As SHDocVw.InternetExplorer = msWinShellDirectoryEntry.Key()
                            Dim msWinShellDirectoryInfo As DirectoryInfo = msWinShellDirectoryEntry.Value()
                            AddHandler msWinShellIExplorerWindow.NavigateComplete2, New SHDocVw.DWebBrowserEvents2_NavigateComplete2EventHandler(Sub(pDisp As Object, ByRef URL As Object)
                                                                                                                                                     navigateCompleteCount += 1
                                                                                                                                                     If (navigateCompleteCount.Equals(msWinShellIExplorerWindowsLockingCurrentDirectory.Count())) Then
                                                                                                                                                         With [DirectoryInfo]
                                                                                                                                                             .Delete(recursive)
                                                                                                                                                             .Refresh()
                                                                                                                                                         End With
                                                                                                                                                     End If
                                                                                                                                                 End Sub)
                            msWinShellIExplorerWindow.Navigate2(New Uri(msWinShellDirectoryInfo.Root.FullName()).AbsoluteUri())
                        Next
                    Else
                        With [DirectoryInfo]
                            .Delete(recursive)
                            .Refresh()
                        End With
                    End If
                Catch ex As Exception
                End Try

                [DirectoryInfo].Refresh()
                If ([DirectoryInfo].Exists() AndAlso (retryCount <= maxRetryCount)) Then
                    [DirectoryInfo].TrySafeDelete(recursive, retryCount + 1)
                End If
                success = Not DirectoryInfo.Exists()
            End If
        End If
        Return success
    End Function
Selfdetermination answered 14/11, 2013 at 21:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.