The StoryLast week, one of my fellow programmers rightfully suggested that we could make our application more robust with the following feature. Whenever the application needs a file ( or directory ) and it doesn’t find it, it could popup a screen to the user allowing her to manually search for the file. Better still, it could even go and search for it automatically. In the latter case, we would know what we were looking for and we could scan the disks the user wanted us to scan. If after all this, we where still not able to find the file, we do as we do now : dump a log message and exit painfully. The weeks before, we had been correcting some reported bugs and for me it was time to start something new. Our application is quite big and it needs a lot of files and directories: various databases, configuration files, import and export paths, help file paths, .... It was clear to me from the start that there was a reusable component in the making. First of all, there are some API’s available for searching for files. I know these API’s, but I wanted more. I wanted a component that is uniform in searching for files. Whether you search for files with some extension, some size, some structure or some contents, the component should be called the same for all of them. IFileCriteriumWhatever our component would look like, it would need to separate the good from the bad (and the ugly). This leads us to a very simple interface : Public Function FileOK(
ByVal aFile As file) As Boolean For those of you who wonder, the ‘file’ datatype comes from the Microsoft Scripting Runtime (aka scrrun.dll) (a reference you can normally add to your project). The interface looks very simple and of course, that was the intention. We’ll see later that it is not yet complete, but for the moment it will do. Our first job now is to create some “stock” implementations for this interface. They will -to a great extent- make the component more or less usable. Without them, a user of our component would be forced to implement them herself. The most trivial of them is the cFileCritNameLike class. It will take a name pattern and return true for all files of which the name matches the pattern. It looks like this : Private mNamePattern As String Implements iFileCriterium
Friend Sub Initialize(ByVal strNamePattern As String) mNamePattern = strNamePattern Debug.Assert (mNamePattern <> "") If mNamePattern = "" Then mNamePattern = "*" End Sub
Private Function iFileCriterium_FileOK(ByVal aFile As Scripting.IFile) As Boolean Debug.Assert Not (aFile Is Nothing) iFileCriterium_FileOK = (aFile.Name Like mNamePattern) End Function None of this code is quite spectacular. I do have some comments to add :
Since performance is of high (but not highest) importance in this component, I have written a seperate class cFileCritNameExactly class. To balance the criteria, I have also written cFileCritExtensionLike and cFileCritExtensionExactly. The only benefit of the separation of these classes is that you only pay the pattern matching price when you really have to. In a good tradition of my other components, it is clear that there is no need at all for any of these classes to be public. We only need some factory methods to create them with the correct parameters but after that, we are only interested in the interface they implement. Another obvious file criterium is the cFileCritAttributes. It takes some attributes to match and only lets the files that match them pass the “gate”. I could have written more boilerplate implementations, but for the moment I did not do this. Suffice it to say that the Scripting.File is a very rich object and you can easily write your own implementations and use them with the component. Recursing the directoriesThen it was time to start writing the real code that did the search. My first attempt was : Sub FindFiles(ByVal strStartPath as string, byval objFilter as iFileFilter) It didn’t take me long to see that this was a little too unambitious. You could only search files in a given subdirectory. If you wanted to search files on multiple drives or in multiple folders, you have a problem. with this limited method signature. Furthermore, why should you limit yourself to searching for files. Why not folders ? Or Drives ? Hm. Finding an answer is easy if you ask the right questions. Searching for files and folders implied iFolderCriteria and iDriveCriteria. That was exactly what I did. The definitions of the interfaces are nearly exactly the same as IFileCriterium. The following table summarizes the classes.
Equipped with file-, folder- and drivecriteria, it became trivial to come up with the following functions : Event FileFound (byval whichFile as File)
Higher-order criteriaThe benefit of an article is that you usually write it after writing a component. This means that it looks like you knew everything in advance and it was just the typing of the code that took time. As most of you know, it’s never like that. I started writing higher-order criteria from the moment I had finished writing the base file criteria. These allowed me to combine the file criteria in the usual ways : and, or, not. I had a very bad feeling when I wrote the same things for Folders and Drives. Why ? It’s easier with an example :
How to use it.So far for the component. Now how do you use it ? Currently the class is written such that it should be used in a form. In your form, you could write the following code :
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Subitems : | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Site updated : Monday, February 17, 2003
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||