How Many EXE Files Do You Have?
By Tom Cone, Tampa PC Users Group
One thing that's kept me interested in personal computing is the seemingly never-ending potential it offers for doing tedious chores quickly and easily. Basically, I guess I'm pretty lazy. If there's something the PC can do faster and more accurately than I can do by hand, I'll spend some time and effort to "make it happen". I also enjoy the challenge of pushing the technology a bit, using the PC to solve problems on my own. This combination of personal "quirks" has led me to learn a little about programming, and to use that knowledge from time to time to solve an occasional "puzzle". Sometimes the investment in the puzzle's "solution" is greater than the value of the solution. In other cases, like the one I'm going to describe in this article, you can sometimes avoid "reinventing the wheel", by relying on advice from folks who share your interests, and are a step or two ahead of you on the PC "learning curve".
Recently I learned that our editor, William LaMartin, was embarked on a programming project using Visual BASIC. He was building a short program that would scan his entire hard drive and give him a count of the number of "EXE" files installed on the drive. This struck me as a problem ideally suited for a computerized solution. I liked the problem because I could understand it. At least I thought I did. What was needed was a way of opening every folder, scanning and counting any EXE files found therein, then closing the folder, and moving on to the next folder. If you repeated the cycle for every folder, you'd have an accurate tally of all EXE files on the drive.
So, I dropped William an email note and asked if he would mind sending me what he had developed so far. He quickly sent me the source code, and looking it over I began to see how he was approaching the problem. Although I don't know Visual BASIC, it began to look like there were several tricky little "details" that had to be solved in order to come up with a correct solution. First, you needed a reliable way of figuring out where all the folders were located on the hard drive. As you know, folders (directories) can hold folders (subdirectories), which can hold folders (sub-subdirectories) which have folders (sub-sub-subdirectories) inside, also. How can you be sure you've scanned each and every folder on the hard drive? I began to see that this "riddle" could be trickier than I had imagined.
Next, you need a way to scan the file directory within each folder, one entry at a time, and test whether an entry meets the search criteria, or not, e.g. look at the first filename, is it an EXE file? If so, count it, if not, skip it and go to the next one, and so one.
This task seemed to be working well for William, but he told me that he wasn't all that happy with how he had figured out how to find all the folders on the hard drive. Using specific Visual BASIC commands he built a list of "level one" folders, each of which would then be opened and scanned. He then opened the level one folders and built a list any sub-folders found therein. These became "level two" folders, which were processed similarly, and so on. He counted seven levels "deep" noting that eight levels of sub-folders provided no additional hits.
I began to think of ways to solve the problem using a different programming language, Visual dBASE and quickly ran into the same difficulty as had William. The program must tell the computer which folders to open and scan, but the number and location of folders is unknown to the programmer when the program begins to run. I began to work on a solution which opened the root directory, built a list of all "primary" folders located there, and then processed them in series. As each primary folder is opened and scanned, a new list of sub-folders located within, if any, is built. These would then be processed one at a time, in series. I wanted to process all the folders in a single "root" stemming from the root directory. When a "stem" in the root directory had been carried to its logical extreme, I would eventually open a folder and find zero folders within it. I would know that all folders in that particular branch of the directory "tree" would have been checked, regardless of how many sub-folder levels might exist. If I repeated the process for all "stems" in the root directory, I would have covered, comprehensively, all folders on the hard drive.
Doing this proved very, very difficult. If a folder could only hold one sub-folder the problem would be soluble, at least by me. However, since multiple subdirectories can exist anywhere along the "stem", I began to flounder. My "directory" tree resembled an ancestral family tree so much that I began to wonder if I hadn't stumbled into a "genealogical" quagmire, and almost called Jenny Lind, our Geneology SIG Leader, to see if she had any suggestions for me!
Instead, I asked myself what William would do? I got on the Internet and asked for help. This was done by posting an email message in a VisualDBASE newsgroup. Within the space of several hours I received a really great suggestion. Why not look "outside the box" and take advantage of services available from the operating system? Huh? Well, it turns out that the lowly DOS "DIR" command, in its Windows 95 incarnation, contains a lot of power that I'd not been using.
We've all gone to the command line, typed "DIR" and pressed the ENTER key to see a list of files in the current directory, right? Some of us know that if you add "/p" after the command, the display will pause instead of scrolling quickly through and off the top of your screen. Remember, also, that you can limit the display to files which match a particular pattern. "DIR *.EXE", for example, will list only files that have the letters "EXE" in their extension.
In the distant past I also remember that many DOS commands permit you to "redirect" output from the display screen to other devices, like the printer, or the hard disk. For instance, the command "DIR > C:\FILELIST.TXT", when executed from the command line, will scan the current directory, and instead of displaying the file list on screen, will "redirect" the file list to a DOS text file called "FILELIST.TXT" which will be written in your root directory on the C: drive. Try it yourself, but be warned, any earlier file by the same name (FILELIST.TXT) will be "overwritten" by the file created in this way by the DIR command.
This much was familiar to me, but its usefulness for the EXE file count problem was not apparent until the additional command line arguments "/S" and "/B" were shown to me.
The Command "DIR /S" will list all files in the current directory, and all files in any subdirectories found within it. If this were executed from the root directory it would display a list of all files on the hard drive, including those in any subdirectories, sub-subdirectories, and so on. The Command "DIR /B" will list data on each file "Briefly", excluding information like date created, date last modified, and so forth. All you get is the filename, including its DOS path.
If you go to the command line in the root directory and type the DOS command DIR C:\*.EXE /S/B > C:\EXE.TXT , you create a DOS Text file called "EXE.TXT" in your root directory which contains a list of every single EXE file on your hard drive. Once this file is created, all my program has to do is open the list, count the number of lines, and display the answer
In VisualDBASE I did this by building a program which shuts itself down temporarily, runs the DOS DIR command with the necessary arguments to build a text file containing the EXE filenames, and then opens an array, reads the file list from the disk file one line at a time, each line going into a separate element in the array, and when the end of file is reached, I see how big my array is, and display the result. It would run a bit faster if I didn't build the array, and simply counted lines in the text file, but I wanted a more general solution which permits me to actually display the filenames on screen from within the program. This solves the problem comprehensively, but makes more experienced programmers shudder. My approach is "quick and dirty", lacking the elegance which solving my "folders within folders" dilemma without using the crutch of a temporary disk file would have meant. But it works, works quickly, and is accurate and thorough.
Naturally, I shared "my" approach with William, managing to create the impression, without actually lying about it, that I simply recalled these arcane command line arguments for the DOS DIR command from memory, instead of having them "fed" to me by more experienced programmers on the Internet!
But you've heard enough from me, he'll finish the story for himself.
William LaMartins additional comments:
Using Toms approach, here is what my program reduced to:
Private Sub Command1_Click()
Dim MyAppID
If Dir("C:\TEMP\EXE-2.TXT") <> "" Then Kill "C:\TEMP\EXE.TXT"
MyAppID = Shell("C:\command.com /C DIR C:\*.EXE /S/B > C:\TEMP\EXE.TXT", 1) ' Run DOS DIR comand
start = Timer
Do While Timer - start < 40 'give time for the DOS program to run
Loop
Open "C:\TEMP\EXE-2.TXT" For Input As #1
Do Until EOF(1)
Line Input #1, A$
List1.AddItem A$
Loop
Close
Text1.Text = List1.ListCount
End Sub
This is much shorter than my first attempt. There is one fly still left in the ointment, though. Notice the Timer loop that waits 40 seconds. That is to allow time for the DOS program to finish cataloguing all the EXE files before the VB program begins to display them. It would be more elegant to have the main program check to see when the DOS program has finished and then to display all the files that had been found. Unfortunately VB does not have the ability to do this. Thus, we need to step out of the box, as Tom calls it, and appeal to the Win 95 operating system with what are known as Win32API calls. This was beyond my current knowledge, but Tom pointed me to a site on the WWW that had such information. Modifying what I found there to fit my situation, I was able to create a program with code about twice as long as the one above which did the job elegantly with no need for the timer.
This is just one more example of how we members can help each other and the usefulness of the WWW. u