SOL9 2.0 Class: FolderWatcher

 SOL9 C++ Class Library  SOL9 Samples  SOL9 Tutorial  SOL9 FAQ  SOL9 ClassTree 

Source code

/*
 * FolderWatcher.h 
 * Copyright (c) 2012 Antillia.com TOSHIYUKI ARAI. ALL RIGHTS RESERVED. 
 */


// SOL9
// 2008/09/17
// 2008/12/10
// Added an optional 'flags' parameter to specify filters for
// ReadDirectoryChangesW API to the contructor 'FolderWatcher'.
// 2012/06/22

#include <sol\Thread.h>
#include <sol\String.h>

namespace SOL {

/**
 * FolderWatcher is a thread class to watch changes of a directory (including all 
 * subdirectories). In thread procedure 'run' method, we use Windows API ReadDirectoryChangesW()
 * 
 */

class FolderWatcher: public Thread {

private:
    String directory;

private:
    HANDLE hFolder;
private:
    bool    looping;

private:
    HANDLE terminateEvent;

private:
    //2008/12/10
    DWORD filters;

public:
    FolderWatcher(const TCHAR* dir=_T("C:\\"),
        DWORD flags=FILE_NOTIFY_CHANGE_DIR_NAME | 
                    FILE_NOTIFY_CHANGE_FILE_NAME | 
                    FILE_NOTIFY_CHANGE_LAST_ACCESS |
                    FILE_NOTIFY_CHANGE_ATTRIBUTES | 
                    FILE_NOTIFY_CHANGE_SIZE    | 
                    FILE_NOTIFY_CHANGE_LAST_WRITE|
                    FILE_NOTIFY_CHANGE_CREATION | 
                    FILE_NOTIFY_CHANGE_SECURITY )
    :Thread(),
     directory(dir),
     hFolder(INVALID_HANDLE_VALUE),
     looping(false),
     terminateEvent(NULL),
     filters(flags) {
        //
        terminateEvent = CreateEvent(0,FALSE,FALSE,0);

    }

public:
    ~FolderWatcher() {
        close();
    }

private:
    bool close()
    {
        bool rc = false;
        if (this->hFolder != INVALID_HANDLE_VALUE) {
            //To stop this thread, close handle hFolder;
            //It will cause an error in ReadDirectoryChangesW()
            CloseHandle(this->hFolder);
            this->hFolder = INVALID_HANDLE_VALUE;
            rc = true;
        }
        if (terminateEvent) {
            CloseHandle(terminateEvent);
            terminateEvent = NULL;
        }
        return rc;
    }

public:
    // Set terminateEvent to stop run loop.
    void stop() {
        SetEvent(terminateEvent);
        looping  = false;
    }

public:
    //Added on 2008/12/10
    const TCHAR* getDirectory()
    {
        return (const TCHAR*)directory;
    }

public:
    //Print out action and filename to console.In order to customize or change output,
    //define your own subclass and redfine this mehtod in that class.
    virtual void changedFileName(const wchar_t* action, const wchar_t* filename)
    {
        wprintf(L"%s: %s\n", action,  filename);
    }


public:
    /**
     * Thread procedure
     */
    void run()
    {
        looping = true;

        const TCHAR* dir = (const TCHAR*)directory;
        if (GetFileAttributes(dir) == 0xffffffff) {
            //2008/09/24 Should be changed to be able to make multiple subdirectories
            if (CreateDirectory(dir, NULL)) {
                _tprintf(_T("Create a new directory %s\n"), dir); //2012/06/22
            }
        }

        this->hFolder = CreateFile(dir,
              FILE_LIST_DIRECTORY,
              FILE_SHARE_READ|FILE_SHARE_DELETE,
              NULL,
              OPEN_EXISTING,
              // Specify FILE_FLAG_OVERLAPPED flags
            FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, 
              NULL);

        if(this->hFolder == INVALID_HANDLE_VALUE){
            _tprintf(_T("Failed to open dir %s\n"), dir); //2012/06/22
            return;
        }

        OVERLAPPED ol; 
        ol.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 
        HANDLE waitEvents[2];
        waitEvents[0] = terminateEvent;
        waitEvents[1] = ol.hEvent;

        //Start a loop of call ReadDirectoryChangesW().
        while(looping) {

            DWORD dwBytes = 0;
            TCHAR buf[1024];

            BOOL rc = ReadDirectoryChangesW(hFolder, buf, 1022,1,
                this->filters,
/*
//2008/12/10
                FILE_NOTIFY_CHANGE_DIR_NAME | 
                FILE_NOTIFY_CHANGE_FILE_NAME | 
                FILE_NOTIFY_CHANGE_LAST_ACCESS |
                FILE_NOTIFY_CHANGE_ATTRIBUTES | 
                FILE_NOTIFY_CHANGE_SIZE    | 
                FILE_NOTIFY_CHANGE_LAST_WRITE|
                FILE_NOTIFY_CHANGE_CREATION | 
                FILE_NOTIFY_CHANGE_SECURITY,
*/            
                &dwBytes, &ol, NULL);

            if (rc == FALSE) {
                printf("Failed:ReadDirectoryChanges()\n");
                break;
            }
            if (looping == false) {
                printf("looping ends\n");
                break;
            }
            
            int r = WaitForMultipleObjects(2, waitEvents, FALSE, INFINITE ); 
            if (r == WAIT_OBJECT_0 ) {
                //printf("Stopped 0\n"); 
                printf("\nTerminateEvent has been set, so break this thread loop\n");
                break;
            }

            BOOL b = GetOverlappedResult(hFolder, &ol, &dwBytes, TRUE ); 
            if (b == FALSE) {
                continue;
            }

            //printf("buf ReadSize=%d\n",dwBytes);

            FILE_NOTIFY_INFORMATION *fn = (FILE_NOTIFY_INFORMATION *)buf;

            while (fn) {
                //FileName will be "NULL" terminated 
                fn->FileName[fn->FileNameLength/2] = 0;

                wchar_t * action = L"";

                switch(fn->Action){

                case FILE_ACTION_ADDED:
                    action = L"Added";
                    break;
                case FILE_ACTION_REMOVED:
                    action = L"Removed";
                    break;
                case FILE_ACTION_MODIFIED:
                    action = L"Accessed/Modified";
                    break;
                case FILE_ACTION_RENAMED_OLD_NAME:
                    action = L"Renamed(Old name)";
                    break;
                case FILE_ACTION_RENAMED_NEW_NAME:
                    action = L"Renamed(New name)";
                    break;
                default:
                    action = L"(Unknown)";
                }

                changedFileName(action,  fn->FileName);

                if (fn->NextEntryOffset == NULL) {
                    break;
                } else {
                        fn = (FILE_NOTIFY_INFORMATION *)(((char *)fn) + fn->NextEntryOffset);
                }
            } //while

        }

        CloseHandle(ol.hEvent);

        close();

    }
};

}

Last modified: 3 Jul 2012

Copyright (c) 2009-2012 Antillia.com ALL RIGHTS RESERVED.