Q: How can I monitor a given folder to see, for example, when a file was added, deleted, or modified?

A: An easy way is to use FindFirstChangeNotification, FindNextChangeNotification and one of the wait functions in a worker thread. As for example:

Code:
DWORD WINAPI FolderFilesWatchDogThread(LPVOID lpParam) // thread procedure
{
   HANDLE hFileChange = 
      ::FindFirstChangeNotification((LPCTSTR)lpParam, // folder path
                                    FALSE,            // don't look in subfolders
                                    FILE_NOTIFY_CHANGE_FILE_NAME); 
                                                      // watch for
                                                      // renaming, creating, 
                                                      // or deleting a file
   if(INVALID_HANDLE_VALUE == hFileChange)
   {
      DWORD dwError = ::GetLastError();
      // handle error (see this FAQ)
      return dwError;
   }
   
   while(TRUE)
   {
      ::WaitForSingleObject(hFileChange, INFINITE);
      // Bark, bark!!! A file was renamed, created or deleted.
      ::FindNextChangeNotification(hFileChange);
   }
   return 0;
}

   // somewhere in the space...
   ::CreateThread(NULL, 0, FolderFilesWatchDogThread, _T("c:\\temp"), 0, NULL);
A little bit more sophisticated but offering more info (like the name of the file) is to use ReadDirectoryChangesW.
Also, here is a simplified example:

Code:
DWORD WINAPI FolderWatchThread(LPVOID lpParam) // thread procedure
{
   HANDLE hDirectory =
      ::CreateFile((LPCTSTR)lpParam,    // folder path 
                   FILE_LIST_DIRECTORY,
                   FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
                   NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
   
   if(INVALID_HANDLE_VALUE == hDirectory)
   {
      DWORD dwError = ::GetLastError();
      // handle error (see this FAQ)
      return dwError;
   }

   DWORD dwBytesReturned = 0;
   const DWORD dwBuffLength = 4096;
   BYTE buffer[dwBuffLength];
   WCHAR wchFileName[_MAX_PATH + 1];
   
   while(::ReadDirectoryChangesW(hDirectory, buffer, dwBuffLength, FALSE,
                                 FILE_NOTIFY_CHANGE_FILE_NAME, &dwBytesReturned, 
                                 NULL, NULL))
   {
      DWORD dwNextEntryOffset = 0;
      PFILE_NOTIFY_INFORMATION pfni = NULL;
      do
      {
         pfni = (PFILE_NOTIFY_INFORMATION)(buffer + dwNextEntryOffset);
         
         switch(pfni->Action)
         {
         case FILE_ACTION_ADDED: 
            // The file was added to the directory. 
            break;
         case FILE_ACTION_REMOVED: 
            // The file was removed from the directory. 
            break;
         case FILE_ACTION_RENAMED_OLD_NAME: 
            // The file was renamed and this is the old name. 
            break;
         case FILE_ACTION_RENAMED_NEW_NAME: 
            // The file was renamed and this is the new name.
            break;
            // ...
         }
         memcpy(wchFileName, pfni->FileName, pfni->FileNameLength);
         wchFileName[pfni->FileNameLength / sizeof(WCHAR)] = L'\0';
         // Enjoy of added, removed, or modified file name...
         dwNextEntryOffset += pfni->NextEntryOffset; // next please!
      }while(pfni->NextEntryOffset != 0);
   } 
   ::CloseHandle(hDirectory);
   return 0;
}