Thursday, March 22, 2007

File Change Notifications

File Change Notifications

HANDLE parameter passed to ::WaitForSingleObject can be a "file change notification handle." The Win32 API includes a
function named ::FindFirstChangeNotification that returns a handle you can use to wake a blocked thread whenever a change
occurs in a specified directory or its subdirectories—for example, when a file is renamed or deleted or a new directory is
created.

Changes to the file system are instantly reflected in the left or right pane. The most efficient way to do it is to start a
background thread and have it block on one or more file change notification handles. Here's what the thread function for a
thread that monitors drive C: might look like:

UINT ThreadFunc (LPVOID pParam)
{
HWND hwnd = (HWND) pParam; // Window to notify
HANDLE hChange = ::FindFirstChangeNotification (_T ("C:\\"),
TRUE, FILE_NOTIFY_CHANGE_FILE_NAME ¦ FILE_NOTIFY_CHANGE_DIR_NAME);

if (hChange == INVALID_HANDLE_VALUE) {
TRACE (_T ("Error: FindFirstChangeNotification failed\n"));
return (UINT) -1;
}

while (...) {
::WaitForSingleObject (hChange, INFINITE);
::PostMessage (hwnd, WM_USER_CHANGE_NOTIFY, 0, 2);
::FindNextChangeNotification (hChange); // Reset
}
::FindCloseChangeNotification (hChange);
return 0;
}




The first parameter passed to ::FindFirstChangeNotification identifies the directory you want to monitor, the second
specifies whether you want to monitor just that directory (FALSE) or that directory and all its subdirectories (TRUE), and
the third specifies the kinds of changes that the thread should be notified of. In this example, the thread will be awakened
when a file is created, renamed, or deleted anywhere on the C: drive (FILE_NOTIFY_CHANGE_FILE_NAME) or when a directory is
created, renamed, or deleted (FILE_NOTIFY_CHANGE_DIR_NAME). When the thread is awakened, it posts a user-defined message to
the window whose handle was passed in pParam. The message's lParam holds a drive number (2 for drive C:). The window that
receives the message—presumably the application's top-level frame window—can respond to the message by updating its views.
Keep in mind that a thread awakened by a file change notification doesn't receive any information about the nature of the
change or about where in the directory tree the change occurred, so it must scan the file system if it wants to determine
what caused the file change notification.

It's also possible to structure the thread so that it monitors not just one drive, but several. All you would have to do is
call ::FindFirstChangeNotification once per drive to acquire a separate file change notification handle for each drive and
use ::WaitForMultipleObjects to block on all the file change notifications simultaneously. ::WaitForMultipleObjects is the
Win32 API equivalent of CMultiLock::Lock. Passing FALSE in the third parameter to a call to ::WaitForMultipleObjects tells
the system to wake the thread when any one of the objects that the thread is blocking on becomes signaled.

No comments: