Windows Directory Management 6

 

 

 

 

 

Obtaining Directory Change Notifications Program Example

 

An application can monitor the contents of a directory and its subdirectories by using change notifications. Waiting for a change notification is similar to having a read operation pending against a directory and, if necessary, its subdirectories. When something changes within the directory being watched, the read operation is completed. For example, an application can use these functions to update a directory listing whenever a file name within the monitored directory changes.

An application can specify a set of conditions that trigger a change notification by using the FindFirstChangeNotification() function. The conditions include changes to file names, directory names, attributes, file size, time of last write, and security. This function also returns a handle that can be waited on by using the wait functions. If the wait condition is satisfied, FindNextChangeNotification() can be used to provide a notification handle to wait on subsequent changes. However, these functions do not indicate the actual change that satisfied the wait condition. Use FindCloseChangeNotification() to close the notification handle. To retrieve information about the specific change as part of the notification, use the ReadDirectoryChangesW() function. This function also enables you to provide a completion routine. The following example monitors the directory tree for directory name changes. It also monitors a directory for file name changes. The example uses the FindFirstChangeNotification() function to create two notification handles and the WaitForMultipleObjects() function to wait on the handles. Whenever a directory is created or deleted in the tree, the example should update the entire directory tree. Whenever a file is created or deleted in the directory, the example should refresh the directory. This simplistic example uses the ExitProcess() function for termination and cleanup, but more complex applications should always use proper resource management such as FindCloseChangeNotification() where appropriate.

Create a new empty Win32 console application project. Give a suitable project name and change the project location if needed.

 

Obtaining Directory Change Notifications Program Example: Creating new Win32 C++ console application project in Visual C++ .NET

 

Then, add the source file and give it a suitable name.

Next, add the following source code.

 

#include <windows.h>

#include <stdio.h>

// Safer versions

#include <strsafe.h>

 

// Function prototypes

// A prototype that receives a function name, displaying

// system error code and its respective message

void DisplayErrorBox(LPTSTR lpszFunction);

void RefreshDirectory(LPTSTR);

void RefreshTree(LPTSTR);

void WatchDirectory(LPTSTR);

 

int wmain(int argc, WCHAR *argv[])

{

    if(argc != 2)

    {

        wprintf(L"Change notification for directory\n");

        wprintf(L"Usage: %s <dir>\n", argv[0]);

        return 1;

    }

 

    WatchDirectory(argv[1]);

    return 0;

}

 

void DisplayErrorBox(LPTSTR lpszFunction)

{

    // Retrieve the system error message for the last-error code

    LPVOID lpMsgBuf;

    LPVOID lpDisplayBuf;

    DWORD dw = GetLastError();

 

    FormatMessage(

        FORMAT_MESSAGE_ALLOCATE_BUFFER |

        FORMAT_MESSAGE_FROM_SYSTEM |

        FORMAT_MESSAGE_IGNORE_INSERTS,

        NULL,

        dw,

        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),

        (LPTSTR) &lpMsgBuf,

        0, NULL );

 

    // Display the error message and clean up

    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(WCHAR));

    StringCchPrintf((LPTSTR)lpDisplayBuf, LocalSize(lpDisplayBuf) / sizeof(WCHAR),  L"%s failed with error %d: %s", lpszFunction, dw, lpMsgBuf);

    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, L"Error", MB_OK);

 

    LocalFree(lpMsgBuf);

    LocalFree(lpDisplayBuf);

}

 

void WatchDirectory(LPTSTR lpDir)

{

   DWORD dwWaitStatus;

   HANDLE dwChangeHandles[2];

   WCHAR lpDrive[4];

   WCHAR lpFile[_MAX_FNAME];

   WCHAR lpExt[_MAX_EXT];

 

   // Breaks a path name into components. These are versions of _splitpath(),

   // _wsplitpath() with security enhancements

   _wsplitpath_s(lpDir, lpDrive, 4, NULL, 0, lpFile, _MAX_FNAME, lpExt, _MAX_EXT);

 

   lpDrive[2] = '\\';

   lpDrive[3] = '\0';

  

   // Watch the directory for file creation and deletion

   dwChangeHandles[0] = FindFirstChangeNotification(

      lpDir,                         // directory to watch

      FALSE,                    // do not watch subtree

      FILE_NOTIFY_CHANGE_FILE_NAME); // watch file name changes

 

   if (dwChangeHandles[0] == INVALID_HANDLE_VALUE)

   {

     DisplayErrorBox(L"FindFirstChangeNotification() 1");

     ExitProcess(GetLastError());

   }

 

   // Watch the subtree for directory creation and deletion

   dwChangeHandles[1] = FindFirstChangeNotification(

      lpDrive,                        // directory to watch

      TRUE,                          // watch the subtree

      FILE_NOTIFY_CHANGE_DIR_NAME);  // watch dir name changes

 

   if (dwChangeHandles[1] == INVALID_HANDLE_VALUE)

   {

     DisplayErrorBox(L"FindFirstChangeNotification() 2");

     ExitProcess(GetLastError());

   }

  

   // Make a final validation check on our handles

   if ((dwChangeHandles[0] == NULL) || (dwChangeHandles[1] == NULL))

   {

     wprintf(L"\nFindFirstChangeNotification() - unexpected NULL.\n");

     ExitProcess(GetLastError());

   }

  

   // Change notification is set. Now wait on both notification handles and refresh accordingly

   while (TRUE)

   {

         // Wait for notification

         wprintf(L"\nWaiting for notification...\n");

 

      dwWaitStatus = WaitForMultipleObjects(2, dwChangeHandles, FALSE, INFINITE);

 

      switch (dwWaitStatus)

      {

         case WAIT_OBJECT_0:             

             // A file was created, renamed, or deleted in the directory.

             // Refresh this directory and restart the notification

             RefreshDirectory(lpDir);

             if (FindNextChangeNotification(dwChangeHandles[0]) == FALSE )

             {

               DisplayErrorBox(L"FindNextChangeNotification()");

               ExitProcess(GetLastError());

             }

             break;

 

         case WAIT_OBJECT_0 + 1:

             // A directory was created, renamed, or deleted. Refresh the tree and restart the notification

             RefreshTree(lpDrive);

             if (FindNextChangeNotification(dwChangeHandles[1]) == FALSE )

             {

               DisplayErrorBox(L"FindNextChangeNotification()");

               ExitProcess(GetLastError());

             }

             break;

 

         case WAIT_TIMEOUT:

            // A timeout occurred, this would happen if some value other

            // than INFINITE is used in the Wait call and no changes occur.

            // In a single-threaded environment you might not want an INFINITE wait

            wprintf(L"\nNo changes in the timeout period.\n");

            break;

 

         default:

            wprintf(L"\n ERROR: Unhandled dwWaitStatus.\n");

            ExitProcess(GetLastError());

            break;

      }

   }

}

 

void RefreshDirectory(LPTSTR lpDir)

{

   // This is where you might place code to refresh your

   // directory listing, but not the subtree because it would not be necessary

   wprintf(L"Directory %s changed!\n", lpDir);

}

 

void RefreshTree(LPTSTR lpDrive)

{

   // This is where you might place code to refresh your directory listing, including the subtree.

   wprintf(L"Directory tree %s changed!\n", lpDrive);

}

 

Build the project. Create several empty text or other files in any folders of C drive. For example, in this case, two files was created as shown below in C:\

 

Obtaining Directory Change Notifications Program Example: Creating files for testing purpose

 

Run the program. The following screenshot shows a sample output.

 

Obtaining Directory Change Notifications Program Example: A sample console program output in action recording the file changes

 

By leaving the output console active, rename test.txt to test1.txt. Notice the output.

 

Obtaining Directory Change Notifications Program Example: A sample console program output in action when there are changes to the folder/file

 

Then delete the testingfile.doc file. Notice the output.

 

Obtaining Directory Change Notifications Program Example: Another sample console program output in action

 

When there is an error, the output gives some useful meaning.

 

Obtaining Directory Change Notifications Program Example: A sample console program output in action with error tha emit meaningful message

 

 

 

 

< Windows Directory 5 | Win32 Programming | Win32 Directory Index | Windows Directory 7 >