filewatch.cc.svn-base

来自「SumatraPDF是一款小型开源的pdf阅读工具。虽然玲珑小巧(只有800多K」· SVN-BASE 代码 · 共 294 行

SVN-BASE
294
字号
// By william blum, 2008#include "SumatraPDF.h"#include "FileWatch.h"#include "file_util.h"#include <sys/types.h>#include <sys/stat.h>#include <assert.h>#include <time.h>#include "tstr_util.h"#include "base_util.h"// Get the directory name from a full file path and copy it to pszDirbool GetDirectory (LPCTSTR pszFile, PTSTR pszDir, size_t cchDir){    LPCTSTR pszBaseName = FilePath_GetBaseName(pszFile);    if (0 == pszDir || 0 == pszFile) {        return false;    }    if (!tstr_copyn(pszDir, cchDir, pszFile, pszBaseName-pszFile)) {        return false;    }    // Is the file located in the root directory?    if (pszDir[pszBaseName-pszFile-2] == ':') {        // add the backslash at the end        pszDir[pszBaseName-pszFile-1] = '\\';        pszDir[pszBaseName-pszFile] = '\0';    }    return true;}// Abort simultaneously all the watching thread and wait until they are all stopped.void SimultaneousSynchronousAbort(int nfw, FileWatcher **fw){    // Preparing to exit the program: ask the children thread to terminate    HANDLE *hp = new HANDLE[nfw];    int k = 0;    for(int i=0; i<nfw;i++) {        if (fw[i]->hWatchingThread) {            // send a message to the stop the watching thread            SetEvent(fw[i]->hEvtStopWatching);            hp[k++] = fw[i]->hWatchingThread;        }    }    // wait for the two threads to end    WaitForMultipleObjects(k, hp, TRUE, INFINITE);    for(int i=0; i<nfw;i++) {        if (fw[i]->hWatchingThread) {            CloseHandle(fw[i]->hWatchingThread);            fw[i]->hWatchingThread = NULL;        }    }    delete hp;}bool FileWatcher::IsThreadRunning(){    return hWatchingThread && (WaitForSingleObject(hWatchingThread, 0) == WAIT_TIMEOUT);}// Ask for the thread to stop and waith until it endsvoid FileWatcher::SynchronousAbort(){    SetEvent(hEvtStopWatching);    if (hWatchingThread)    {        WaitForSingleObject(hWatchingThread, INFINITE);        CloseHandle(hWatchingThread);        hWatchingThread = NULL;    }}void FileWatcher::RestartThread(){    // if the thread already exists then stop it    if (IsThreadRunning())        SynchronousAbort();    DWORD watchingthreadID;    // reset the hEvtStopWatching event so that it can be set if    // some thread requires the watching thread to stop    ResetEvent(hEvtStopWatching);    hWatchingThread = CreateThread( NULL, 0,        (LPTHREAD_START_ROUTINE) WatchingThread,        this,        0,        &watchingthreadID);}void FileWatcher::Clean(){    if (overl.hEvent) {        CloseHandle(overl.hEvent);         overl.hEvent = NULL;    }    if (hDir) {        CloseHandle(hDir);        hDir = NULL;    }}void FileWatcher::Init(LPCTSTR filefullpath){    // if the thread already exists then stop it    if (IsThreadRunning())        SynchronousAbort();    tstr_copy(szFilepath, dimof(szFilepath), filefullpath);    pszFilename = FilePath_GetBaseName(szFilepath);    GetDirectory(filefullpath, szDir, dimof(szDir));        _tstat(filefullpath, &timestamp);    callbackparam = 0;    pCallback = NULL;    hDir = CreateFile(        szDir, // pointer to the directory containing the tex files        FILE_LIST_DIRECTORY,                // access (read-write) mode        FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,  // share mode        NULL, // security descriptor        OPEN_EXISTING, // how to create        FILE_FLAG_BACKUP_SEMANTICS  | FILE_FLAG_OVERLAPPED , // file attributes        NULL // file with attributes to copy       );    memset(&overl, 0, sizeof(overl));    memset(buffer, 0, sizeof(buffer));    overl.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);    curBuffer = 0;    // watch the directory    ReadDirectoryChangesW(         hDir, /* handle to directory */         &buffer[curBuffer], /* read results buffer */         sizeof(buffer[curBuffer]), /* length of buffer */         FALSE, /* monitoring option */         //FILE_NOTIFY_CHANGE_CREATION|         FILE_NOTIFY_CHANGE_LAST_WRITE, /* filter conditions */         NULL, /* bytes returned */         &overl, /* overlapped buffer */         NULL); /* completion routine */}// Start watching a file for changesvoid FileWatcher::StartWatchThread(LPCTSTR filefullpath, WATCHCALLBACK cb, LPARAM param){    Init(filefullpath);       callbackparam = param;    pCallback = cb;    RestartThread();}// Thread responsible of watching the directory containg the file to be watched for modificationsvoid WINAPI FileWatcher::WatchingThread( void *param ){    FileWatcher *fw = (FileWatcher *)param;    if (!fw || fw->hDir == NULL) // if no directory to watch then leave        return;     // Main loop    HANDLE hp[2] = { fw->hEvtStopWatching, fw->overl.hEvent};    while (1) {        DWORD dwObj = WaitForMultipleObjects(dimof(hp), hp, FALSE, INFINITE ) - WAIT_OBJECT_0;        assert( dwObj >= 0 && dwObj <= dimof(hp) );        if (dwObj == 0) { // the user asked to quit the program            break;        } else if (dwObj == 1) {        } else {            // BUG!            break;        }        if (fw->ReadDir() && fw->pCallback)            fw->pCallback(fw->szFilepath, fw->callbackparam);    }    fw->Clean();}// Call ReadDirectoryChangesW to check if the file has changed since the last call.bool FileWatcher::HasChanged(DWORD waittime){    if (overl.hEvent == NULL)        return false;    DWORD dwObj = WaitForSingleObject(overl.hEvent, waittime);    if (dwObj == WAIT_OBJECT_0) {        return ReadDir();    }    return false;}// Call the ReadDirectory API and determine if the file being watched has been modified since the last call. // Returns true if it is the case.bool FileWatcher::ReadDir(){    // Read the asynchronous result of the previous call to ReadDirectory    DWORD dwNumberbytes;    GetOverlappedResult(hDir, &overl, &dwNumberbytes, FALSE);    // Switch the 2 buffers    curBuffer = 1 - curBuffer;    // start a new asynchronous call to ReadDirectory in the alternate buffer    ReadDirectoryChangesW(         hDir, /* handle to directory */         &buffer[curBuffer], /* read results buffer */         sizeof(buffer[curBuffer]), /* length of buffer */         FALSE, /* monitoring option */         //FILE_NOTIFY_CHANGE_CREATION|         FILE_NOTIFY_CHANGE_LAST_WRITE, /* filter conditions */         NULL, /* bytes returned */         &overl, /* overlapped buffer */         NULL); /* completion routine */    // Browse the list of FILE_NOTIFY_INFORMATION entries    FILE_NOTIFY_INFORMATION *pFileNotify;    pFileNotify = (PFILE_NOTIFY_INFORMATION)&buffer[1-curBuffer];    while (pFileNotify) {        pFileNotify->FileName[min(pFileNotify->FileNameLength/sizeof(WCHAR), _MAX_FNAME-1)] = 0;        PTSTR pFilename;        #ifdef _UNICODE        pFilename = pFileNotify->FileName;        #else        // Convert the filename from unicode string to oem string        TCHAR oemfilename[_MAX_FNAME];        wcstombs( oemfilename, pFileNotify->FileName, _MAX_FNAME );        pFilename = oemfilename;        #endif        // is it the file that is being watched?        if (stricmp(pFilename, pszFilename) == 0) {            // file modified?            if (pFileNotify->Action == FILE_ACTION_MODIFIED) {#if 0                // Check that the timestamp has changed to prevent spurious notifications to be reported                // compare the old and new time stamps                struct _stat newstamp;                if (_tstat(szFilepath, &newstamp) == 0                    && difftime(newstamp.st_mtime, timestamp.st_mtime) > 0                    ) {                    DBG_OUT("FileWatch:change notification in %s\n", pszFilename);                    // Check that the file has not already been reopened for writing.                    // we try to open the file with write access and no write-sharing                     // so that if the file is opened for writing then the call fails.                    HANDLE hf = CreateFile(this->filepath(), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);                    DWORD dw = GetLastError();                    if (hf == INVALID_HANDLE_VALUE && (dw==ERROR_SHARING_VIOLATION || dw==ERROR_LOCK_VIOLATION))                        return false;                    if (hf != INVALID_HANDLE_VALUE)                        CloseHandle(hf);                                        // reread the time stamp                    //_tstat(szFilepath, &timestamp);                    timestamp = newstamp;                    return true; // the file has changed!                }                else {                    // false positive: the time stamp has not changed                    DBG_OUT("FileWatch:spurious change notification in %s\n", pszFilename);                }#else                 // we do not check for timestamp difference because                // the granularity is so low that it would cause some file notifications to be ignored                // (this happens when compiling a small .tex document with pdftex)                DBG_OUT("FileWatch:change detected in %s\n", pszFilename);                return true;#endif            }            //else {} // file touched but not modified.                    }        // step to the next entry if there is one        if (pFileNotify->NextEntryOffset)            pFileNotify = (FILE_NOTIFY_INFORMATION*) ((PBYTE)pFileNotify + pFileNotify->NextEntryOffset) ;        else            pFileNotify = NULL;    }    return false;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?