Skip to content

How to Recursively Delete a Folder

While Xojo doesn’t currently provide a direct way of recursively deleting a folder, there are various options that will allow you to do this:

1. Recursively iterate through all the folders, deleting each one. You can read about that at our Developer Site, this is the best cross-platform way.
2. Use the Shell to recursively delete a folder (i.e. “del /s” on Windows, or “rm -rf” on OS X and Linux)

Windows specific ways:
3. SHFileOperation (an older API)
4. IFileOperation (the more modern API that Windows recommends over option #3)

I’ve decided to look at option #4, since this allows more customized options, like the ability to show a progress dialog. However, it is also the slightly more complicated option since it deals with COM & Delegates. The example illustrates this:

 // Select a folder to delete
 Dim f As FolderItem = SelectFolder

 // Our one declare that creates our ShellItem
 Declare Function SHCreateItemFromParsingName Lib "Shell32" _
  (path As WString, pbc As Ptr, riid As Ptr, Byref ppv As Ptr) As Integer

 // These IIDs identify the COM object that we're looking for.
 // Unfortunately finding the IIDs is not as simple since
 // they are typically only exposed by C headers or in the type
 // library of the COM component.
 // Normally you would use Project->Add ActiveX component to
 // add COM objects to your project
 Dim CLSID_FileOperation As MemoryBlock = _
     COM.IIDFromString("{3ad05575-8857-4850-9277-11b85bdb8e09}")
 Dim IID_IFileOperation As MemoryBlock = _
     COM.IIDFromString("{947aab5f-0a5c-4c13-b4d6-4bf7836fc9f8}")
 Dim IID_IShellItem As MemoryBlock = _
     COM.IIDFromString("{43826d1e-e718-42ee-bc55-a1e261c37bfe}")
 Dim result As Integer

 Dim shellItem As Ptr
 result = SHCreateItemFromParsingName(f.NativePath, _
              Nil, IID_IShellItem, shellItem)
 If result <> COM.S_OK Then Return

 // fileOp will be how we communicate with the COM component.
 // Basically it will hold a list of function pointers to call
 Dim fileOp As Ptr
 result = COM.CoCreateInstance(CLSID_FileOperation, _
              Nil, COM.CLSCTX_SERVER, IID_IFileOperation, fileOp)
 If result <> COM.S_OK Then Return

 Dim sizeOfPtr As Integer
 #if Target64Bit
   sizeOfPtr = 8
 #else
   sizeOfPtr = 4
 #endif

 // Do not display any UI/dialog
 Const FOF_NO_UI = &h614

 // Since fileOp is just a list of function pointers, we need
 // to call the correct one. Thankfully this information is
 // known (mainly because I have the C headers for them).
 // It can also be gathered by looking at the COM component's
 // type library. I've setup SetOperationFlags, DeleteItem,
 // and PerformOperation as delegates with the correct
 // function prototype

 // Delegate Function SetOperationFlags(this As Ptr,
 //                  flags As UInt32) As Integer
 Dim setOpFlagsFunc As New SetOperationFlags( _
                     fileOp.Ptr(0).Ptr(5 * sizeOfPtr))
 result = setOpFlagsFunc.Invoke(fileOp, FOF_NO_UI)

 // Delegate Function DeleteItem(this As Ptr, 
 //                  shellItem As Ptr, progressSink As Ptr) As Integer
 Dim deleteItemFunc As New DeleteItem(fileOp.Ptr(0).Ptr(18 * sizeOfPtr))
 result = deleteItemFunc.Invoke(fileOp, shellItem, Nil)

 // Delegate Function PerformOperation(this As Ptr) As Integer
 Dim performOpFunc As New PerformOperation(
                     fileOp.Ptr(0).Ptr(21 * sizeOfPtr))
 result = performOpFunc.Invoke(fileOp)

 // This is how we cast a Ptr to a COM object to be released
 Dim unk As New COM.IUnknown(fileOp)
 result = unk.Release

In conclusion, while it’s a lot of work to set this up, it has the added benefit over the other options in that it is more extensible/customizable on the Windows platform.  Here’s a link to download this example.


windows transparent label tip