14

I am trying to delete a folder and all of its sub-folders recursively but it is not working at all, so can someone please check the code and tell me what I am doing wrong here?

I am running this code through D7 under Windows XP

if FindFirst (FolderPath + '\*', faAnyFile, f) = 0 then
      try             
         repeat

            if (f.Attr and faDirectory) <> 0 then
              begin
                    if (f.Name <> '.') and (f.Name <> '..') then
                      begin                            
                        RemoveDir(FolderPath +'\'+ f.Name);
                      end
                    else
                      begin
                        //Call function recursively...
                        ClearFolder(FolderPath +'\'+ f.Name, mask, recursive);
                      end;
              end;

         until (FindNext (f) <> 0);
      finally
        SysUtils.FindClose (f)
      end;
end;
Warren P
  • 65,725
  • 40
  • 181
  • 316
MChan
  • 6,842
  • 27
  • 83
  • 132
  • 1
    How about using DeleteDirectory from JclFileUtils? – Stefan Glienke Aug 03 '12 at 16:12
  • 1
    You didn't even post enough code to give anyone a chance to really debug it, I mean, how can we know what ClearFolder does? Technically the answers below are making assumptions, because you're making people guess. Nevertheless, your question is good, and not a duplicate. Good one. The other similar questions are all too specific, and your title is much more general. Good. +1 for you! – Warren P Aug 03 '12 at 20:34
  • 1
    @Warren the code in the question is the body of ClearFolder. Standard recursion. – David Heffernan Aug 03 '12 at 20:45
  • @WarrenP thanks for taking the time to read my question, DavidHeffernan has helped me and solved the problem – MChan Aug 04 '12 at 22:32
  • Possible duplicate of [Delphi, delete folder with content](https://stackoverflow.com/questions/5716666/delphi-delete-folder-with-content) – Vadzim May 08 '18 at 05:26

3 Answers3

32

Rather than do all this hard work yourself, I'd just use SHFileOperation:

uses
  ShellAPI;

procedure DeleteDirectory(const DirName: string);
var
  FileOp: TSHFileOpStruct;
begin
  FillChar(FileOp, SizeOf(FileOp), 0);
  FileOp.wFunc := FO_DELETE;
  FileOp.pFrom := PChar(DirName+#0);//double zero-terminated
  FileOp.fFlags := FOF_SILENT or FOF_NOERRORUI or FOF_NOCONFIRMATION;
  SHFileOperation(FileOp);
end;

For what it is worth, the problem with your code is that it doesn't ever call DeleteFile. And so the directories are never getting emptied, the calls to RemoveDir fail and so on. The lack of error checking in your code doesn't really help, but adding code to delete files would get that code in half-decent shape. You also need to take care with the recursion. You must make sure that all the children are deleted first, and then the parent container. That takes a certain degree of skill to get right. The basic approach is like this:

procedure DeleteDirectory(const Name: string);
var
  F: TSearchRec;
begin
  if FindFirst(Name + '\*', faAnyFile, F) = 0 then begin
    try
      repeat
        if (F.Attr and faDirectory <> 0) then begin
          if (F.Name <> '.') and (F.Name <> '..') then begin
            DeleteDirectory(Name + '\' + F.Name);
          end;
        end else begin
          DeleteFile(Name + '\' + F.Name);
        end;
      until FindNext(F) <> 0;
    finally
      FindClose(F);
    end;
    RemoveDir(Name);
  end;
end;

I've omitted error checking for the sake of clarity, but you should check the return values of DeleteFile and RemoveDir.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • In fact, I was about to write an essentially identical answer, but I was too slow. – Andreas Rejbrand Aug 03 '12 at 15:46
  • Thanks David for your help. There is a reason why I am doing it the hard way which is that I must do some operations on each of the deleted folders *before* deleting them....so I must loop on them one by one then after finishing whatever I must do with each folder, I must delete it... The API is an outstanding fast solution, which I personally prefer to use but I am stuck here with this solution due to the task requirements :( – MChan Aug 03 '12 at 15:47
  • Ya, you didn't say that in the question. Anyway, I think my update should get you on the way. – David Heffernan Aug 03 '12 at 15:49
  • Sorry if I didn't mention it clearly in my question. I've just tried your updated code, and it didn't erase folders...I am not sure what is wrong here and why those (empty) folders can't simply be erased....but by tracing code I can see that even DeleteFile was never called Any thoughts? – MChan Aug 03 '12 at 15:55
  • Got it! Silly mistake in my code. RemoveDir line was wrong. See update. – David Heffernan Aug 03 '12 at 16:03
  • In code: //it's a directory, empty it ClearFolder(FolderPath +'\' + f.Name, mask, recursive) when this is called on an empty folder nothing happens...I understand and can clearly see that you are using RemoveDir(FolderPath + '\' + f.Name); to remove the empty folder but this never happens, which is confusing me a lot here....I am thinking loudly may be the mask is the problem? I am passing mask as *.*, and I tried using it as * Any thoughts? – MChan Aug 03 '12 at 16:04
  • Yeah, I fixed it now. Like I said, it requires a degree of skill to get right, and it took me two goes to demonstrate that skill!! – David Heffernan Aug 03 '12 at 16:05
  • Even though I don't find a single problem in the code sample you offered, bad news is that none of the folder are deleted :( I did my best to debug and see what could be wrong and I can't find anything....the folders are simply not deleted using the code...any thoughts? – MChan Aug 03 '12 at 16:20
  • I was trying to debug, didn't want just to come back and report that it was not working, so I wanted to see what could be possibly the reason but couldn't find any as mentioned in my previous comment – MChan Aug 03 '12 at 16:21
  • I updated the code. Look at the line that calls `RemoveDir`. It's different from your version I bet. There was an error in a previous version of my code. I tested this code. P.S. Good for you for trying to work it out yourself. That's an excellent sign! – David Heffernan Aug 03 '12 at 16:22
  • The version I was using was the last version of code you posted :) I know you updated it but it didn't work even with all my trials :( – MChan Aug 03 '12 at 16:26
  • It works fine here. So it would seem that `RemoveDir` is failing for you. Perhaps some process has those folders locked. Now you need to add some error checking. I expect `RemoveDir` returns false. If so call GetLastError to find out why. – David Heffernan Aug 03 '12 at 16:30
  • David, I think it's better to use the PathDelim constant instead of the '\' literal. – jachguate Aug 03 '12 at 17:00
  • 2
    @jachguate: Marginally, since the above code is very native to the Win32 API, anyway. – Andreas Rejbrand Aug 03 '12 at 17:03
  • @jachguate I was copying the style of the code in the Q. I'd prefer TPath.Combine in fact. The point of the code here is to get the recursion issues right. – David Heffernan Aug 03 '12 at 17:11
  • @Andreas I know it's Win32, but anyway I think it's a good practice. – jachguate Aug 03 '12 at 17:52
  • @DavidHeffernan It finally worked but only by combining the SHFileOperation inside the code you wrote, instead of using RemoveDir. When I tried GetLastError, while using RemoveDir I got the message access dinied...folder used by another process, which I think was because of Explorer.exe since I had the drive containing the folder opened in explorer while executing the code What was really strange is that I got (invalid handle) error when I tried to delete the folders using SHFileOperation but they were *deleted* – MChan Aug 04 '12 at 22:29
7
procedure DeleteDir(const DirName: string);
var
  Path: string;
  F: TSearchRec;

begin
  Path:= DirName + '\*.*';
  if FindFirst(Path, faAnyFile, F) = 0 then begin
    try
      repeat
        if (F.Attr and faDirectory <> 0) then begin
          if (F.Name <> '.') and (F.Name <> '..') then begin
            DeleteDir(DirName + '\' + F.Name);
          end;
        end
        else
          DeleteFile(DirName + '\' + F.Name);
      until FindNext(F) <> 0;
    finally
      FindClose(F);
    end;
  end;
  RemoveDir(DirName);
end;
kludg
  • 27,213
  • 5
  • 67
  • 118
  • How does this differ from the code in my answer? The main thing I can see is that the RemoveDir is outside the `if FindFirst` but that cannot make any difference. So why have you produced an answer with essentially the exact same code as is in my answer. And also not even bothered to explain what it is all about? I'm confused. – David Heffernan Aug 03 '12 at 16:41
  • 3
    @DavidHeffernan - This is a community site, everybody is free to send questions & answers. I did not really seen your last updates, just written & tested my code. – kludg Aug 03 '12 at 16:45
  • 1
    OK, I understand, it just looked a little odd that the code was essentially identical to mine, and appeared some while later. – David Heffernan Aug 03 '12 at 16:53
  • 1
    @DavidHeffernan - I am not interested in SO rephunting if you mean it, spending a little time on SO, posting short answers sometimes that may be helpful to the questioner. – kludg Aug 03 '12 at 17:05
  • 3
    I suppose having two identical tested answers shows the questioner that the code works – David Heffernan Aug 03 '12 at 17:12
1

Starting with Delphi 2010 there is a TDirectory record in System.IOUtils unit with some methods, including

TDirectory.Delete('path_to_dir', True);
Marcodor
  • 4,578
  • 1
  • 20
  • 24