-1

I'm using Windows API to recursively delete many files and folders. I'm using it without a UI and suppressing errors. The problem is, it completely fails if one of those files is in use. I expect that possibility, and want this to continue anyway, skipping any such cases. The one file which fails is actually the same EXE which is calling this delete command (which will be deleted after it's all done anyway.

Here's what I'm doing now:

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;

How can I make this skip any event of a file being in use? I looked at the documentation but can't find anything that can do this.

Jerry Dodge
  • 26,858
  • 31
  • 155
  • 327

2 Answers2

0

Here is just an idea you can implement in your function to validate if there is any file in use:

function IsFileInUse(FileName: TFileName): Boolean;
var
  HFileRes: HFILE;
begin
  Result := False;
  if not FileExists(FileName) then
    Exit;

  HFileRes := CreateFile(PChar(FileName),
                         GENERIC_READ or GENERIC_WRITE,
                         0,
                         nil,
                         OPEN_EXISTING,
                         FILE_ATTRIBUTE_NORMAL,
                         0);
  Result := (HFileRes = INVALID_HANDLE_VALUE);

  if not Result then
    CloseHandle(HFileRes);
end;

Maybe it can help you.

R.P Silveira
  • 372
  • 2
  • 10
  • 1
    How would this interact with the call to `SHFileOperation`? – David Heffernan May 12 '14 at 14:21
  • I'm using it from a different way. I use a loop in all files in directory, then I check if the current file is in use before delete it. As I said, it's just an idea. – R.P Silveira May 12 '14 at 14:26
  • 1
    In that case you would just call `DeleteFile` and ignore the return value. If the file cannot be deleted, it won't be and `DeleteFile` will return true. Trying to second guess it like this is rather pointless. – David Heffernan May 12 '14 at 14:36
  • Yes, calling `DeleteFile` is the correct way. Calling `CreateFile` will also modify the last access time, which is not intended... and even if `CreateFile` succeeds, it might fail in `DeleteFile`... – Jochen Kalmbach May 12 '14 at 18:05
  • No -1 but this doesn't solve what I'm doing. In the end, it's not necessary to do this check, as Windows automatically fails anyway. The problem is, using the method I am, I can't figure out how to make it continue deleting everything else automatically and skip anything that's in use. – Jerry Dodge May 12 '14 at 20:21
0

Why don't you try this:

procedure DeleteFiles(const DirName: String);
var
  SR: TSearchRec;
  i: Integer;
begin
  //get all files in directory
  i := FindFirst(DirName +'\*.*', faAnyFile, SR);

  while i = 0 do
  begin
    if (SR.Attr and faDirectory) <> faDirectory then
      DeleteFile(DirName +'\'+ SR.Name);

    i := FindNext(SR);
  end;

  FindClose(SR);
end;

It's another way to do it.

R.P Silveira
  • 372
  • 2
  • 10