The Client Side of File Notification


The File Notification Server (NOTIFY.VBP) encapsulates the file change notification in an ActiveX application that you can use from any program. You can see how it works in the Browse Picture Files program (BROWSE.VBP), which acts as a client of the File Notification Server. The program allows you to browse through picture files, seeing and hearing each of them. You can see the program at work in Figure 11-7. It not only displays icons, cursors, bitmaps, meta­files, wave files, and AVI clips, but it also copies, deletes, renames, and moves them. And it updates its display when other processes remove, create, or modify picture files or directories.



Figure 11-7. Browsing pictures.


The Browse Picture Files program receives file notifications from a CFileNotify object by way of an IFileNotifier interface. The key declarations for this connection are at the top of the main client form:

' Create an object that notifies client of file changes
Private notify As CFileNotify

' Implement an interface that connects to CFileNotify
Implements IFileNotifier

The notify object is the EXE server that will use Win32 file notification API functions to watch for any changes to files or directories. But the client has to tell the server object which directories to watch by calling its Connect and Disconnect methods. Whenever the user changes a directory in the browser, it uses the following code to disconnect the old directory and connect the new one:

Private Sub dirPic_Change()
With notify
' Synchronize the file control and select the first file
filPic.Path = dirPic.Path
If filPic.ListCount > 0 Then filPic.ListIndex = 0

' Watch whole drive for directory changes
If hNotifyDir <> -1 Then .Disconnect hNotifyDir
hNotifyDir = .Connect(Me, dirPic.Path, _
FILE_NOTIFY_CHANGE_DIR_NAME, False)
' Watch current directory for name changes (delete, rename, create)
If hNotifyFile <> -1 Then .Disconnect hNotifyFile
hNotifyFile = .Connect(Me, dirPic.Path, _
FILE_NOTIFY_CHANGE_FILE_NAME, False)
' Watch current directory for modifications of file contents
If hNotifyChange <> -1 Then notify.Disconnect hNotifyChange
hNotifyChange = .Connect(Me, dirPic.Path, _
FILE_NOTIFY_CHANGE_LAST_WRITE, False)
End With
End Sub

Skip the first few lines that handle normal directory changes, and concentrate on the code that connects the FBrowsePictures form to the server. First you disconnect any previous connection, and then you connect the server to the new directory. The first connection tells the server to report any changes to the current directory. The last Boolean parameter indicates whether to check child directories. Windows 95 doesn’t support True, so you should supply False if you want the client to work on all platforms. Changes include any directories that have been created, removed, or renamed. The next connection looks for any files whose names have changed in the current directory. Deleting a file or creating a new file obviously changes the filename. The third connection looks for files that have been modified. If you change the current image file with ImagEdit or some other tool, the change appears in the Browse Picture Files program as soon as you save the file.


Notice that each call to Connect passes the form object in the first parameter. The Connect method actually takes an IFileNotifier type for this parameter, but since the form implements IFileNotifier, it is an IFileNotifier and can be early-bound to the server. In the previous edition of this book, the form was passed through an Object parameter. The server had to trust that the client would implement an object with the correct methods. The IFileNotifier interface makes the connection fast and type-safe. The interface consists of a single method:

' Interface for CFileNotify to communicate with its clients
Sub Change(sDir As String, efn As EFILE_NOTIFY, fSubTree As Boolean)

End Sub

Here’s how the FBrowsePictures form implements IFileNotifier:

Private Sub IFileNotifier_Change(sDir As String, _
efn As FileNotify.EFILE_NOTIFY, _
fSubTree As Boolean)
BugMessage "Directory: " & sDir & _
" (" & efn & ":" & fSubTree & ")" & sCrLf
Select Case efn
Case FILE_NOTIFY_CHANGE_DIR_NAME, FILE_NOTIFY_CHANGE_FILE_NAME
Dim i As Integer
' Refresh drive, directory, and file lists
i = filPic.ListIndex
filPic.Refresh
filPic.ListIndex = IIf(i, i - 1, i)
dirPic.Refresh
drvPic.Refresh
Case FILE_NOTIFY_CHANGE_LAST_WRITE
' Refresh current picture in case it changed
filPic_Click
End Select
End Sub

This code is a little sloppy, although there’s no harm done in this case. It ­refreshes everything in sight rather than trying to figure out exactly what changed. You might be able to be more exact in your code. Now let’s look at where those notification calls come from.