/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include "FileWatcher.h" #include <Windows.h> #include <functional> struct FolderRootWatch::PlatformImplementation { PlatformImplementation() : m_directoryHandle(nullptr), m_ioHandle(nullptr) { } HANDLE m_directoryHandle; HANDLE m_ioHandle; }; ////////////////////////////////////////////////////////////////////////////// /// FolderWatchRoot FolderRootWatch::FolderRootWatch(const QString rootFolder) : m_root(rootFolder) , m_shutdownThreadSignal(false) , m_fileWatcher(nullptr) , m_platformImpl(new PlatformImplementation()) { } FolderRootWatch::~FolderRootWatch() { // Destructor is required in here since this file contains the definition of struct PlatformImplementation Stop(); delete m_platformImpl; } bool FolderRootWatch::Start() { m_platformImpl->m_directoryHandle = ::CreateFile(m_root.toStdWString().data(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, nullptr); if (m_platformImpl->m_directoryHandle != INVALID_HANDLE_VALUE) { m_platformImpl->m_ioHandle = ::CreateIoCompletionPort(m_platformImpl->m_directoryHandle, nullptr, 1, 0); if (m_platformImpl->m_ioHandle != INVALID_HANDLE_VALUE) { m_shutdownThreadSignal = false; m_thread = std::thread(std::bind(&FolderRootWatch::WatchFolderLoop, this)); return true; } } return false; } void FolderRootWatch::Stop() { m_shutdownThreadSignal = true; CloseHandle(m_platformImpl->m_ioHandle); m_platformImpl->m_ioHandle = nullptr; if (m_thread.joinable()) { m_thread.join(); // wait for the thread to finish m_thread = std::thread(); //destroy } CloseHandle(m_platformImpl->m_directoryHandle); m_platformImpl->m_directoryHandle = nullptr; } void FolderRootWatch::WatchFolderLoop() { FILE_NOTIFY_INFORMATION aFileNotifyInformationList[50000]; QString path; OVERLAPPED aOverlapped; LPOVERLAPPED pOverlapped; DWORD dwByteCount; ULONG_PTR ulKey; while (!m_shutdownThreadSignal) { ::memset(aFileNotifyInformationList, 0, sizeof(aFileNotifyInformationList)); ::memset(&aOverlapped, 0, sizeof(aOverlapped)); if (::ReadDirectoryChangesW(m_platformImpl->m_directoryHandle, aFileNotifyInformationList, sizeof(aFileNotifyInformationList), true, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_FILE_NAME, nullptr, &aOverlapped, nullptr)) { //wait for up to a second for I/O to signal dwByteCount = 0; if (::GetQueuedCompletionStatus(m_platformImpl->m_ioHandle, &dwByteCount, &ulKey, &pOverlapped, INFINITE)) { //if we are signaled to shutdown bypass if (!m_shutdownThreadSignal && ulKey) { if (dwByteCount) { int offset = 0; FILE_NOTIFY_INFORMATION* pFileNotifyInformation = aFileNotifyInformationList; do { pFileNotifyInformation = (FILE_NOTIFY_INFORMATION*)((char*)pFileNotifyInformation + offset); path.clear(); path.append(m_root); path.append(QString::fromWCharArray(pFileNotifyInformation->FileName, pFileNotifyInformation->FileNameLength / 2)); QString file = QDir::toNativeSeparators(QDir::cleanPath(path)); switch (pFileNotifyInformation->Action) { case FILE_ACTION_ADDED: case FILE_ACTION_RENAMED_NEW_NAME: ProcessNewFileEvent(file); break; case FILE_ACTION_REMOVED: case FILE_ACTION_RENAMED_OLD_NAME: ProcessDeleteFileEvent(file); break; case FILE_ACTION_MODIFIED: ProcessModifyFileEvent(file); break; } offset = pFileNotifyInformation->NextEntryOffset; } while (offset); } } } } } }