Finding files, part 4

by Mark G. Wiseman

Actually, this article should be entitled “Deleting the files we found”. In the first three parts of this series, I showed you the code I wrote to find specific files on a computer. I wanted to write a utility program to find and delete the unnecessary files created by C++Builder.

Once you’ve found the files, deleting them is actually very easy. In this article I’ll present only the code I wrote to delete the files. If you’d like to see the whole thing tied together, you can download the example program from the Bridges Publishing Web site.

 

Choosing an approach

There are a couple of different ways to approach deleting files in Windows. The VCL has a function DeleteFile() that just calls the Windows API function of the same name.

The other approach is to use the Windows shell to delete the files. I chose this approach because the shell gives me the option of deleting the files outright or sending them to the Windows Recycle Bin. Also, if used correctly, the shell will prompt the user for confirmation before deleting files and show a progress dialog while deleting files.

I created a class called TDeleteFiles to do the work. The code for TDeleteFiles is in Listings A and B. Let’s see what the code does.

 

Going, going, gone…

The TDeleteFiles class encapsulates the Windows shell function SHFileOperation() and the structure SHFILEOPSTRUCT. This is done completely within the Delete() function of TDeleteFiles.

The SHFileOperation() relies on information contained within SHFILEOPSTRUCT to copy, move, rename or delete files. The wFunc member of SHFILEOPSTRUCT takes a value that tells SHFileOperation() which operation to perform. We will set wFunc to FO_DELETE to delete files.

Although I could use SHFileOperation() to delete each file as it is found, this function really shines when it is passed a list of files in the pFrom member of SHFILEOPSTRUCT. When given a list of files and with the proper flags set, SHFileOperation() will prompt the user for confirmation before deleting files and will show a progress dialog while performing the deletion. If you have used the Windows Explorer, you have seen the dialog boxes shown in Figures 1 and 2. I’ll discuss how to build a list of files later in the article.

The fFlags member of SHFILEOPSTRUCT accepts several flags that control the behavior of SHFileOperation(). The three flags we need to use are FOF_ALLOWUNDO, FOF_NOCONFIRMATION and FOF_SILENT.

 

Figure A: The Windows 2000 Delete Files confirmation dialog box.

IMG00003.gif

 

Figure B: The Windows 2000 Delete Files progress dialog box.

IMG00004.gif

 

When the FOF_ALLOWUNDO flag is set, files are sent to the Recycle Bin instead of being permanently deleted. TDeleteFiles sets this flag by default and provides a property, AllowUndo that can be used to set or read this flag.

The FOF_NOCONFIRMATION flag, when set, will cause SHFileOperation() to not show the Confirmation dialog before deleting files. To be safe, TDeleteFiles does not set this flag by default. The property that controls this flag is NoConfirm.

If the FOF_SILENT flag is set, SHFileOperation() will not show the progress dialog while deleting files. SHFIleOperation() can take several seconds to delete a large number of files, so this flag is not set by default. The corresponding property in TDeleteFiles is Silent.

Once all of the members of SHFILEOPSTRUCT are zeroed or are filled with meaningful values, SHFileOperation() is called with a pointer to the SHFILEOPSTRUCT as its only argument. If there is an error, SHFileOperation() returns a nonzero value. The Delete() function reflects the return value of SHFileOperation() by returning true if the operation was successful and false if SHFileOperation() reported an error.

If the user should cancel the operation before SHFileOperation() has completed, the fAnyOperationsAborted member of SHFILEOPSTRUCT is set to true. This information can be accessed through the read-only property UserCancelled in TDeleteFiles.

 

The file list

As I mentioned, the pFrom member of SHFILEOPSTRUCT will accept a list of files. The list is an array of characters. Each file in the list must be null terminated (“\0”) and the entire list must have a double null termination (“\0\0”).

AnsiString will allow embedded null characters, so I used an AnsiString variable, named fileList, to store the list of files within TDeleteFiles. The Add() function takes a file name as an AnsiString argument and appends it to the end of fileList along with a null character. When the Delete() function is called, it adds the extra null character to the end of fileList before assigning fileList to pFrom using c_str().

The Clear() function empties fileList by setting it to an empty string.

 

Conclusion and warning

In this series of articles I have shown you how to build a utility that will search for unnecessary project files on your computer and delete them. The code in the utility, both for deleting files and for finding files should be useful for other applications as well.

If you download the example program, you will notice that it is set, by default, to “Preview Only”. Think about it. If you are building an application that has the potential to delete all the files on your computer, you need to be very careful. So, my advice to you is to be very careful. And, have fun.

 

Listing A: DelFiles.h.

#ifndef DelFilesH
#define DelFilesH

class TDeleteFiles
{
  public:
    TDeleteFiles();

    void Add(const String &file);
    void Clear();

    bool Delete();

    __property bool AllowUndo = 
      {read = allowUndo, write = allowUndo};
    __property bool NoConfirm = 
      {read = noConfirm, write = noConfirm};
    __property bool Silent = 
      {read = silent, write = silent};
    __property bool UserCancelled = 
      {read = userCancelled};

  private:
    String fileList;

    bool allowUndo, noConfirm;
    bool silent, userCancelled;
};

#endif   // DelFilesH

 

Listing B: DelFiles.cpp.

#include <vcl.h>
#pragma hdrstop

#include "DelFiles.h"

TDeleteFiles::TDeleteFiles()
{
  allowUndo = true;
  noConfirm = false;
  silent = false;
  userCancelled = false;
}

void TDeleteFiles::Add(const String &file)
{
  fileList += file + String('\0');
}

void TDeleteFiles::Clear()
{
  fileList = "";
}

bool TDeleteFiles::Delete()
{
  if (fileList.IsEmpty()) return(true);

  fileList += String('\0');

  SHFILEOPSTRUCT op;
  ZeroMemory(&op, sizeof(SHFILEOPSTRUCT));
  op.hwnd = Application->MainForm->Handle;
  op.wFunc = FO_DELETE;
  if (allowUndo) 
    op.fFlags |= FOF_ALLOWUNDO;
  if (noConfirm) 
    op.fFlags |= FOF_NOCONFIRMATION;
  if (silent) 
    op.fFlags |= FOF_SILENT;
  op.pFrom = fileList.c_str();

  bool error = SHFileOperation(&op) != 0;

  userCancelled = op.fAnyOperationsAborted;

  Clear();

  return(error);
}

#pragma package(smart_init)