Hidden treasures of Sysutils, Part 4
by Mark G. Wiseman
In Parts 1 and 2 of “Hidden treasures of Sysutils”, I talked about variables and functions that worked with times, dates, numbers, and currency. In Part 3, I introduced you to functions that work with file names. In this article, I will discuss the functions in Sysutils that actually manage files and directories.
So, if these treasures are hidden in Sysutils, how did I find them? I simply looked at the source code. The files SYSUTILS.HPP and SYSUTILS.PAS provided the initial clues (function and variable names) and using these clues I was able to look up most of these treasures in the online help.
Directory functions
There are four functions in Sysutils that you can use to work with directories:
AnsiString GetCurrentDir(); bool SetCurrentDir(const AnsiString Dir); bool CreateDir(const AnsiString Dir); bool RemoveDir(const AnsiString Dir);
The GetCurrentDir() function returns an AnsiString containing the fully qualified path name of the current directory. Its counterpart, SetCurrentDir() takes an AnsiString specifying a directory and attempts to make that directory current. If SetDurrentDir() is successful, it returns true. If the call fails, SetCurrentDir() returns false. The argument to SetCurrentDir() may be a fully qualified path name, or a path name relative to the current directory: For example:
// fully-qualified path
bool success =
SetCurrentDir("c:\\windows\\system");
// relative to the current directory
bool success = SetCurrentDir("system");
The CreateDir() function takes an AnsiString argument specifying a directory path and tries to create that directory. A return value of true indicates success. The argument to CreateDir() will only work if all the subdirectories in a directory path exist except for the lowest subdirectory. In the following line of code, CreateDir() will only succeed if the directory “c:\testing\part4” already exists.
bool success = CreateDir( "c:\\testing\\part4\\subtest");
The final directory function I want to talk about is RemoveDir(). This function takes an AnsiString specifying an empty directory and attempts to remove it. If it cannot remove the directory, RemoveDir() returns false. Note that the directory must be empty or the function will fail.
Disk information functions
There are two functions in Sysutils that help you find out about the size of and free space on a disk.
int64 DiskFree(Byte Drive); __int64 DiskSize(Byte Drive);
Both of these functions accept a Byte argument that represents the drive to query for information. If this argument is 0, the functions query the current drive. Argument values of 1–26 correspond to drives A–Z.
The return value for both functions is an __int64, which is large enough to work with today’s large hard drives. If either function should encounter an error—a non-existent drive, for example—the value -1 is returned.
Directory search functions
There are four functions in Sysutils that allow you to search a directory for specific files. They are:
int FindFirst(const AnsiString Path, int Attr, TSearchRec &F); int FindNext(TSearchRec &F); void FindClose(TSearchRec &F); AnsiString FileSearch( const AnsiString Name, const AnsiString DirList);
The first three of these functions are light wrappers around the Windows API functions FindFirstFile(), FindNextFile() and FindClose(). They are used to search through a specific directory for files matching a given file name pattern and attributes.
These three functions use the TSearchRec structure to hold data about the files they find. Unfortunately, the TSearchRec structure introduces a serious flaw that makes the functions less useful than you might think. The Size member of this structure holds the file size. Size is declared as an int, not an __int64. This means that for very large files (greater than 4 GB), the size of the file may be reported incorrectly.
For this reason, I recommend that you use the Windows API functions. They are only slightly more difficult to use and they will report very large file sizes correctly. If, however, you are still interested in using the functions in Sysutils, there is an example in the program that accompanies this article and also a good example in the VCL help for FindFirst().
The fourth function, FileSearch(), is a very useful function. This functions searches through a list of directories for a file name. It takes two AnsiString arguments: a file name and a semicolon-delimited list of directories. If FileSearch() finds the file, it returns an AnsiString with the fully qualified path to the file. If there is no match for the file name in any of the directories, FileSearch() returns an empty AnsiString. For example:
AnsiString fileName = "notepad.exe"; AnsiString dirList = "c:\\;c:\\Windows;c:\\WinNT;" "c:\\Program Files\\Accessories"; AnsiString path = FileSearch(filename, dirList);
This code searches for NOTEPAD.EXE in the four directories specified in the dirList variable. On my system, a value of “c:\Windows\notepad.exe” is returned in the variable path.
File date functions
There are several functions in Sysutils that you can use to work with file dates. Some of them are:
int FileGetDate(int Handle); int FileSetDate(int Handle, int Age); int FileAge(const AnsiString FileName); System::TDateTime FileDateToDateTime(int FileDate); int DateTimeToFileDate( System::TDateTime DateTime);
The FileGetDate() and FileSetDate() functions work with a file handle on an open file to get and set the date of the file.
FileGetDate() takes a file handle as an argument and returns an int containing a DOS date-time value. If an error is encountered, FileGetDate() returns -1.
FileSetDate() takes an int argument for the file handle, and a second int argument containing a DOS date-time value. It attempts to set the file’s date using the value passed in the second parameter. If unsuccessful, FileSetDate() returns -1.
As you probably know, Windows maintains three dates for a file: the creation date, the date the file was last accessed, and the date the file was last modified. You might be wondering which of these three file dates, the preceding functions work with. The Borland online help says it’s the DOS file date which, when translated to Windows, means the last modified date.
The FileAge() function, like FileGetDate(), returns the last modified date of file. Unlike FileGetDate(), FileAge() works on closed files. It takes an AnsiString argument containing the file name and returns a DOS date-time value.
The FileDateToDateTime() and DateTimeToFileDate() functions can be used to convert a DOS date-time value to a TDateTime and vice versa.
File attribute functions
There are two functions that will retrieve and set the attributes of a file:
int FileGetAttr( const AnsiString FileName); int FileSetAttr( const AnsiString FileName, int Attr);
FileGetAttr() takes an AnsiString argument containing a file name, and returns an int. The return value can be a combination of the values faReadOnly, faHidden, faSysFile, faVolumeID, faDirectory, and faArchive.
FileSetAttr() also takes an AnsiString representing a file name, and an int containing a combination of attributes. If FileSetAttr() is successful, it returns 0. Yes, that’s right a zero on success.
File rename and delete functions
There are two files in Sysutils that you can use to rename and delete files. These functions are (you guessed it!):
bool DeleteFile( const AnsiString FileName); bool RenameFile(const AnsiString OldName, const AnsiString NewName);
Both functions return true if they’re successful and false if they’re not.
File read, write, and seek functions
The last group of functions I want to tell you about are functions you can use to create, open, read, write, and seek into files. All of these functions are thin wrappers around Windows API functions.
int FileCreate( const AnsiString FileName); int FileOpen(const AnsiString FileName, unsigned Mode); int FileRead(int Handle, void *Buffer, unsigned Count); int FileWrite(int Handle, const void *Buffer, unsigned Count); void FileClose(int Handle); int FileSeek(int Handle, int Offset, int Origin); __int64 FileSeek(int Handle, const __int64 Offset, int Origin);
The FileCreate() function accepts a file name as an AnsiString argument and returns a file handle (an int) to a file that is open and ready for reading and writing. A return value of 1 indicates an error.
FileOpen() takes two arguments: an AnsiString containing a file name and an unsigned int representing the mode for opening the file. The mode can be one of several values including fmOpenRead and fmOpenReadWrite. For a complete list of modes, see the online help.
The FileRead() function takes as arguments a file handle, a pointer to a buffer, and an unsigned int that represents the number of bytes to be read into the buffer. FileRead() returns an int that indicates the number of bytes actually read into the buffer. This function reads from the current position in the file buffer.
The FileWrite() function takes a file handle, a void pointer to raw data, and an int specifying how many bytes of the raw data should be written into the file at the current buffer position. FileWrite() returns an int that is the number of bytes written, or 1 if there was an error.
The FileSeek() function is an overloaded function. One version uses arguments and return values of int. The other uses __int64 for very large files. Both functions take a file handle as their first argument, an offset value as the second argument, and an int representing the origin of the offset as the third. Using these arguments, FileSeek() moves the file pointer. The file pointer is the spot in a file where the next byte will be written or the next byte will be read.
The offset argument is either an int or an __int64. The functions return either an int or an __int64, which will contain the new position of the file pointer or 1 if an error occurred.
The origin argument, an int in both versions, should be 0 to offset the file pointer from the beginning of the file, 1 to offset the pointer from its current position, or 2 to offset the pointer from the end of the file.
The following code creates a file named TESTFILE.TXT, writes a line of text to the file, and then closes the file. Next it opens the file for reading only, seeks to a point in the file where the words “some text” are located, and reads those words into a buffer.
String fileName = "TestFile.txt";
int handle = FileCreate(fileName);
if (handle == -1) return;
char text[] =
"This is some text for our test file.";
int error =
FileWrite(handle, text, strlen(text));
if (error == -1)
{
FileClose(handle);
return;
}
FileClose(handle);
handle = FileOpen(fileName, fmOpenRead);
if (handle == -1)
return;
// Seek in 8 bytes from
// the beginning of the file
int pos = FileSeek(handle, 8, 0);
if (pos == -1)
{
FileClose(handle);
return;
}
// Read 9 bytes into buffer
count = FileRead(handle, buffer, 9);
buffer[count] = 0;
Conclusion
Well, I guess it’s about time to close the file on Sysutils. I have just a couple of suggestions for you before I do.
First, there are even more useful functions for dealing with files and file names. You can find these in the FILECTRL.HPP header file. In particular, look at the MinimizeName(), DirectoryExists() and ForceDirectories() functions.
Second, explore the VCL source code that Borland has included with C++Builder. You will find useful classes and functions that you didn’t know were there. These are indeed hidden treasures.