Required: A program to copy all of the files
matching a file mask to a different folder.
Background & Techniques
I recently had need for a procedure to copy all files in a CD folder to a new folder on a hard disk drive
and reset the "ReadOnly" attributes. .
This program was written to test the resulting new "CopyFolder" function, but it
seems that the program has enough features to perhaps serve as a useful utility for
Usage is straight-forward: Select an input folder and an output folder.
The output folder may be existing or a new one. Other
- Set a file mask to control which files get selected. A
separate "file masks" page defines masking string options and provides a
- Set the scope of the operations - files in the input folder only or input folder plus all files in folders contained within the input
- Whether to retain or reset the "readonly" attribute on the
- How to handle duplicate files, i.e. files already existing in the
target folder. options are
- Ignore the file, do not copy.
- Copy the file, replacing the existing file in the the target
- Copy the file only if the source file is newer than the target.
- Ask the user whether to overwrite the existing target file.
Click the "Copy" button to complete the operation. A list of files copied and the
total count will be displayed when the operation has been completed.
You may interrupt the operation by clicking the "Stop" button but the copies
of files already processed will remain in the output folder.
If you select "Ask" option for the Duplicate Files radio group, a reply of
"Cancel" when asked will also stop the operation.
Non-programmers are welcome to read on, but may
want to skip to the bottom of this page to download
executable version of the program.
Notes for Programmers
The program calls the "CopyFolder" function contained in unit
UCopyFolder.pas to do most of the work. CopyFolder
copies files matching a given mask from one folder to another. Parameters
- FromFolder: The path to the folder containing the files to be
- ToFolder: The path to the folder to receiver the files
- Mask: A file mask to control which files are selected ('*.*' =
- DupFileOpts: Four numeric options are available when the file already
- 0 ==> skip the file
- 1 ==> always copy the file
- 2 ==> copy the file if it is newer than the existing copy
- 3 ==> ask the user what action to take
- CopySubFolders: Files in subfolders of the specified
FromPath folder will also be copied if this parameter is true.
- ResetReadOnly: If true, input files marked as "Readonly" will
have that attribute removed in the target location.
- FileExit: The address of optional method (function of object)
specifying a user function to be called before each file is copied.
If the callback procedure is specified, it receives 4 parameters:
- Inpath: Path to the input file.
- OutputPath: Path where the file will be copied.
- Filename: Name of the file to be copied.
- Cancopy: Boolean parameter with a default value of true. Set "Cancopy"
to false to skip copying this file.
FileExit should return a result value of true if copying is to continue, and
false to abort the copy procedure without copying further files.
CopyFolder uses a few techniques worth describing for future
- Recursive search through a folder and
subfolders is a technique we have used before but filtering files
with a file mask makes the procedure a little more complicated.
The FindFirst function can filter returned records based on a
specified file mask, but since we will likely be recursively calling
CopyFolder with subdirectories of the input directory,
FindFirst must search all file names (with a file name mask of
- File mask matching - FindFirst
and successive FindNext calls retrieve file information in
a TSearchrec for each call including the file name, date,
attributes . The Name field is passed to the
MatchesMask function along with the user specified mask. I
could find no user oriented description of mask strings, so I added a tab
sheet to the program with the mask fields described and a few examples.
- Overloading a function or procedure with an
optional method call - I decided to provide an optional callback
function passed as a parameter to CopyFolder. The callback
function is called for each file to be copied, passing the
parameters described above. The typical way to make a parameter
optional is to provide an overloaded version without the optional
parameter and from there call the "real" version with a default
value for the missing parameter. (Delphi recognizes the keyword "overload"
in the procedure definition and will accept multiple definitions with
the same name so long as the parameter lists are different.) The tricky part here is that we
cannot pass a "Nil" (no address) value for the callback function because
the function is defined as a method type function "of object"
meaning that it is a method of an object. Method type
functions are passed as a pair of pointers, a pointer to the function
code and a a pointer to the class instance to which it belongs.
I got around the problem by defining a DummyFileExit function
within a TDummyClass definition. And an instance if
TDummyClass named DummyClass. This DummyfFileExit
may now be passed as the default FileExit for cases where the
user chose not to provide one. CopyFile checks and ignores
any reference to DummyFileExit, so it is not necessary to
actually create the DummyClass instance. Whew!
- Changing file attributes - I only way i
could find to reset the ReadOnly attribute was to copy the (using
the Windows CopyFile funtion) and then use the SetFileAttr
function to reset the attribute if in was set in the input.
Addendum November 10, 2008: At a
user's request, a new option was added to Version 2 of Copy Folder Test.
It is now possible to ignore the folder structure files from
sub-folders of the select input folder and to copy all files directly to the
selected output folder. Additionally counts of files copied,
duplicate files overwritten and duplicate files not copied are kept by the
copy folder functions and are reported by this program at job completion
time. A final new option add "Yes to all" and "No to all"
buttons to the Ask dialog to specify the default action to be taken when
additional duplicate file names are encountered.
December 4, 2008: Version 3 changes
the file date check for the overwrite decision to use "Last modified date"
instead of "Creation date". Seems to make more sense that way as a
user suggested. I also fixed a bug that allowed the program to try to
copy a destination folder which happened to be a subfolder of the source
folder - not a good idea since it would loop forever, or at least until the
disk filled up!
July 15, 2012: Version 3.1 was posted today with three
- An option to process files without actually copying them has been
added. This is to determine the total count and size of zipped
files here on DFF.
- Directories with the "System" attribute are now included in the
directories list so that they can be selected. "Web Site" folders
are one example of types that previously were not selectable. The
fix required correcting an "oversight" in the Delphi 7 FileCtrl
unit which caused the TDirectoryListBox class to exclude these
directories. The modified FileCtrl unit is included in the source
- A change to correctly report file sizes larger than 4GB
previously included in our
List Large Files
utility program is now included here.
May 11, 2013: In the original program I envisioned
performing operations like copying all files in folder "ProgramA" to a new
folder "ProgrtamA_Test". A viewer pointed out it did not work
well for his intended use: i.e. copy folders "ProgramA", "ProgramB", and "ProgramC"
into a folder named "Backups". In other words, copy the folder name as
well as the files in that folder. It seemed like a reasonable request
to me, so Version 3.2 posted today adds a checkbox: "Include selected
input folder record in output" to do just that.
Running/Exploring the Program
Suggestions for Further Explorations
Is there a more direct way to make a method
pointer an optional parameter in a function call?
Original Date: October 30 2006
February 18, 2016