12/96 How-To: Power Windows
Peruse Your PC's Lost-and-Found File
Take inventory of your PC--and the files hiding inside it--with scanning software.
By Karen Kenworthy
Have you ever been dazzled by fancy installation programs that search your entire hard disk? Sometimes they are looking for older versions of the software being installed. Or they may be looking for incompatible programs or drivers.
Searching a disk is a common program chore. DOS made scanning a PC relatively easy via two special functions: FindFirst and FindNext. Programs passed FindFirst the drive and directory where the search was to begin. Then it repeatedly called FindNext. Each call to FindNext provided another file's name, or a value indicating all files had been found.
Few of us are using or writing DOS programs these days. If you program in C or C++ you can still contact DOS and scan disks the old-fashioned way. Visual Basic programmers can use VB's Dir function, which provides the same features as DOS's FindFirst and FindNext. Or we can be a bit more creative.
For example, I've written a small program called WinMag Disk Scanner that lets you select a drive and directory to be searched, then displays the name of every file found in the selected directory or its subdirectories. Along the way, it tabulates and displays the number of files and directories found.
Disk Scanner demonstrates a technique you can use in programs and macros you write. Once a file has been located by your program's version of Scanner, it can open the file, determine the file's size or modification date, or perform other useful acts.
I wrote my program in Visual Basic 3.0. The source code and an executable version of the program are available for download. Look for the archive file PWSCAN.ZIP in the WinMag download locations. You can adapt the program, with few changes to other versions of VB, including Visual Basic 4.0 and Visual Basic, Applications Edition.
Let's look at my program's main window (see the sidebar "Scrutinize the Scanner"). In this window you tell Scanner which drive and directory to search, and Scanner displays its results. In the upper-left corner is a Drive List Box, a Windows control that automatically displays a list of all disk drives attached to a computer. When running Scanner, this list box will let you select the drive to be examined. Immediately below is a Directory List Box. This box automatically lists all the directories on the drive selected above. Scanner will search a selected directory and all its subdirectories. To remind us of their purposes, I've named these list boxes drvIn and dirIn.
On the top right-hand side of the program's main window is a button titled Scan. When clicked, the button executes a subroutine I've written, called Scan, passing it the drive and directory selected in drvIn and dirIn. Scan performs my program's magic, scanning the specified locations.
Below the Scan button is another Directory List Box followed by a new control, a File List Box. This third control automatically displays a list of files found in a particular directory. These two list boxes allow Scan to perform its hard work. For clarity, I've named them dirWork and filWork. Since the user doesn't employ them, these two controls can be made invisible (by setting their Visible property to False).
Once Scan finds a file, the file's name is displayed inside the text box at the bottom of the program's main window. Scan also counts the number of files and directories it finds, and displays those counts in the spaces immediately above the text box. The name of the text box where filenames appear is lblFiles, while the other two text boxes are named lblFileCnt and lblDirCnt.
The scoop on Scan
Now let's look inside the Scan subroutine (see the sidebar "Survey the Code"), which is called when the user clicks on the program's Scan button.
The drive and directory selected by the user from the corresponding list boxes are passed to the subroutine as a text string. In Scan's first line, this parameter is stored in a string variable named Pathname. In lines 2 and 3, Scan declares two variables, i and DirLim. Both are used to temporarily store some numbers.
Lines 4 through 11 are where Scan searches the drive and directory specified in Pathname, counting all the files it finds, and displaying their names. To do that, it makes use of the File List Box we saw earlier, named filWork. Line 4 tells the list box where to look for files, by setting its Path property to the string found in our Pathname variable. Line 5 uses the list box's Pattern property to control which files are displayed. The pattern can be any valid file wildcard specification. In our case we set the Pattern property to "*.*" so we'll see all the directory's files.
Once the Path and Pattern properties are set, Windows fills the File List Box with filenames. The number of files found is stored in the list box's ListCount property.
The name of each file found is also available, via filWork's List property. This property is an array, containing one entry for each file. Individual entries, and therefore individual filenames, can be specified by placing a number inside parentheses after the property name (for example, filWork.List(3)). Unfortunately, the array entries are numbered starting with 0. As a result, the highest numbered entry is equal to ListCount, minus 1. For example, if five files are found, their names will be stored in filWork.List(0) through filWork.List(4).
Lines 6 through 10 are a loop-a group of statements that are executed repeatedly until a specified condition is met. Thanks to line 6, this loop executes once for each filename stored in filWork. This For statement causes the variable name i to be set to 0 the first time the loop is executed. This allows us to examine the list box's first filename. Each successive time through the loop the value of i is increased by 1, until it finally reaches filWork .ListCount - 1 (the array index of the last filename).
Once inside the loop, line 7 extracts the name of a file from the list box and displays it in the lblFiles text box. Line 8 adds one to a variable named FileCnt, where Scan is tracking the number of files found. Line 9 calls Visual Basic's DoEvents function, which allows other programs to perform some work before our program continues. That's a critical function: Without it, all other programs would halt while our program scans the disk.
Finally, once our loop has finished running, we store the number of files found so far in the lblFileCnt text box via line 11.
Lines 1 through 11 take care of the files found in the drive and directory specified in Pathname. But what if a specified directory contains directories of its own?
To obtain a list of those subdirectories, we store Pathname in the Path property of dirWork (see line 12). Windows will then fill the list box with directory names. We can find out how many directories were located by examining dirWork's ListCount property, and see each individual directory name by examining the entries in dirWork's List property array.
But what should we do with each directory found? The answer is simple. We just pass it to Scan, since Scan displays the names of all files found in the directory name passed to it. But can a subroutine call itself?
The answer is yes, thanks to something called recursion. When using modern programming languages, such as Visual Basic, a procedure can call itself. When that occurs, the original call of the procedure is suspended until the new call has finished. The original procedure call then resumes where it left off.
Visual Basic handles most of the details of recursion. It automatically creates new copies of all variables declared within the procedure (except those declared with the Static keyword). It also tracks which statement should execute next. However, our procedure uses two items that Visual Basic can't duplicate-our two Work List Boxes. For that reason, the program must take some special steps to ensure the list boxes' contents are restored each time a subroutine resumes execution after making a recursive call.
First, our programs store the number of directory names found in dirWork in the variable DirLim (in line 13). Then, in lines 14 through 18 it enters a loop that runs once for each directory name found. Line 15 calls Scan, asking it to process the directory found in dirWork.List(i), while line 16 updates our count of directories, stored in the variable DirCnt. Finally, line 17 restores the Path property of dirWork, and therefore its list of directories, which was changed by our call of Scan in line 15.
Line 19 completes the processing of subdirectories by displaying the current direc-tory count in the text box lblDirCnt.
Survey the Code
1 Sub Scan (Pathname As String)
2 Dim i As Integer
3 Dim DirLim As Integer
4 filWork.Path = Pathname
5 filWork.Pattern = "*.*"
6 For i = 0 To filWork.ListCount - 1
7 lblFiles.Caption =
8 FileCnt = FileCnt + 1
10 Next i
11 lblFiles.Caption = "Files: " +
12 dirWork.Path = Pathname
13 DirLim = dirWork.ListCount - 1
14 For i = 0 To DirLim
15 Scan (dirWork.List(i))
16 DirCnt = DirCnt + 1
17 dirWork.Path = Pathname
18 Next i
19 lblDirCnt.Caption = "Directo-
20 End Sub
The Scan subroutine is the heart of WinMag Disk Scanner. (The line numbers above are for informational purposes only.)
Contributing Editor Karen Kenworthy is the author of Visual Basic for Applications, Revealed! (Prima Publishing, 1994) and the manager of WINDOWS Magazine's forums on America Online and CompuServe. Contact Karen in the "Power Windows" topic of these areas, or via e-mail. Karen's e-mail address is firstname.lastname@example.org