The Implementation
The implementation of the TDirectoryMonitor class begins with the TWorkerThread. As mentioned earlier the ReadDirectoryChangesW function can be called synchronously or asynchronously. The synchronous method is the easiest way to use ReadDirectoryChangesW, but there is a problem. The function will not return until an event occurs. This would mean the watch could not be terminated until after an event has occurred. For this reason the TWorkerThread uses the completion port asynchronous call. The completion port method allows the watch to be canceled by calling the PostQueuedCompletionStatus function. Microsoft also provides an example of the completion port option in MSDN. Examples of using ReadDirectoryChangesW synchronously and with a callback function are included in the examples file for this article available for download at my web site:http://www.biggbytesoftware.com/rod/ReadDirectoryChangesDownloads.html
There are two things to focus on in the TWorkerThread. The Create constructor and the Execute method. The TThread Create constructor needs to be replaced because the WorkerThread needs more information. The most obvious piece of information is which directory to monitor. This is passed as the PathToWatch parameter. The next thing the thread will need is to know what events the ReadDirectoryChangesW function will watch for. This is set in the ActionsToWatch parameter. The parameter ParentWindow requires some more explanation.
Since the ReadDirectoryChangesW function is called in a separate thread, the main thread must be notified when one of the events the class is watching for occurs. There are two options to make this notification. The first is to use the Synchronize method of the thread. This will in effect suspend the worker thread until the main thread is done processing the event. Using this method may result in the loss of events. The second option is to send a message to the main thread, but to send a message the worker thread must know where to send the message. That's what the ParentWindow parameter is for. It is the handle of the window that will receive messages generated by the worker thread.
The last parameter is WatchSubFolders. This determines if the ReadDirectoryChangesW function monitors sub-directories of the PathToWatch parameter. The body of the create function assigns all of the parameters to private variables and opens a handle to the directory. The thread is created in a suspended state so there is time to complete all of the setup tasks. The Create constructor implementation is shown in figure 7.
constructor TWorkerThread.Create(const PathToWatch: String; ActionsToWatch: TActionsToWatch;
ParentWindow: THandle; WatchSubFolders : Boolean);
begin
Inherited Create(True);
FWatchSubFolders := WatchSubFolders;
FPathToWatch := PathToWatch;
FActionsToWatch := ActionsToWatch;
FParentWindow := ParentWindow;
FNotifyMask := GetNotifyMask;
FDirHandle := CreateFile(PChar(FPathToWatch),
FILE_LIST_DIRECTORY,
FILE_SHARE_READ Or FILE_SHARE_WRITE Or FILE_SHARE_DELETE,
Nil, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS Or FILE_FLAG_OVERLAPPED,
0);
GetMem(FBuffer,MAX_BUFFER);
FBufferLength := MAX_BUFFER;
Resume;
end
Figure 7 - The TWorkerThread Constructor
Once the constructor has resumed the thread, control is passed to the Execute method. The first thing the Execute method does is open a completion port. All file event notifications will come through this completion port. The result buffer is cleared and ReadDirectoryChangesW is called. After the call to ReadDirectoryChangesW the Execute method enters a Repeat loop and calls GetQueuedCompletionStatus. GetQueuedCompletionStatus will wait for an event and the loop will then call ReadDirectoryChangesW again to wait for the next event until the thread is either terminated or an empty buffer is returned. The buffer is empty when the completion port is shutdown.
Now that the worker thread is completed attention turns to the implementation of the TDriectoryMonitor class itself. As in the TWorkerThread class the TDirectoryMonitor class replaces the ancestor constructor. Then new constructor is shown in figure 8. The main reason for this is so the TDirectoryMonitor can create a window handle to receive the message from the worker thread. This handle is obtained by calling the AllocateHWnd function and passing the address of the message handler procedure for the allocated window. In this case the message handler procedure is named InternalWinProc and is shown in figure 9.
constructorTDirectoryMonitor.Create;
begin
Inherited Create;
FWindowHandle := AllocateHWnd(InternalWinProc);
FWatchSubFolders := True;
end;
Figure 8 - The TDirectoryMonitor Constructor
procedureTDirectoryMonitor.InternalWinProc(var Msg: TMessage);
Const
TranslateActions : Array[FILE_ACTION_ADDED..FILE_ACTION_RENAMED_NEW_NAME] Of TDirectoryAction =
(daFileAdded, daFileRemoved,daFileModified,daFileRenamedOldName,daFileRenamedNewName);
begin
If Msg.Msg = CM_DIRECTORY_EVENT Then
begin
If Assigned(FOnDirectoryChange) then
FOnDirectoryChange(Self,TranslateActions[Msg.wParam], String(Pchar(Msg.LParam)));
StrDispose(PChar(Msg.LParam));
end;
end
Figure 9 – The internal winproc.
The next important method in the TDirecotryMonitor class is the Start method. The start method creates the worker thread that watches the directory in the DirectoryToWatch property. The Stop Method cancels the monitoring by posting the completion port with a zero (null) information value.
- "Monitor Class, Page 1/4"
- "Monitor Class, Page 2/4"
- "Monitor Class, Page 3/4"
- "Monitor Class, Page 4/4"



