Qt实现Windows文件实时监控

FileSystemWatcher.h

#pragma once
#pragma execution_character_set("utf-8")#include #include //观察者线程声明
class WatcherThread;/*
* @class FileSystemWatcher,文件系统观察者
*/
class FileSystemWatcher : public QObject
{Q_OBJECT
public:/** @FileSystemWatcher,构造[重载1]* @param1,父对象*/FileSystemWatcher(QObject* parent = nullptr);/** @FileSystemWatcher,构造[重载2]* @param1,要监控的路径名列表* @param2,要监控的路径,是否监控其包含的子路径*/FileSystemWatcher(const QStringList& pathNameList, const QList& monitorSubpathList = QList(), QObject* parent = nullptr);/** @~FileSystemWatcher(),析构*/~FileSystemWatcher();/** @getLastError,获取最终错误* @return,const QString&*/const QString& getLastError();/** @setMonitorPath,设置监控路径* @param1,要监控的路径名列表* @param2,要监控的路径,是否监控其包含的子路径*/void setMonitorPath(const QStringList& pathNameList, const QList& monitorSubpathList = QList());/** @startMonitor,开始监控* @return,bool*/bool startMonitor();/** @stopMonitor,停止监控* @return,void*/void stopMonitor();/** @restartMonitor,重新开始监控* @return,bool*/bool restartMonitor();/** @getPathNameList,获取路径名列表* @return,const QStringList&*/const QStringList& getPathNameList();/** @getMonitorSubpathList,获取监控子路径列表* @return,const QList&*/const QList& getMonitorSubpathList();protected:/** @setLastError,设置最终错误* @param1,错误信息* @return,void*/void setLastError(const QString& error);private slots:/** @fileChangedSlot,文件改变槽* @param1,文件改变动作* @param2,文件名* @return,void*/void fileChangedSlot(ulong action, const QString& fileName);/** @threadErrorSlot,线程错误槽* @param1,错误信息* @return,void*/void threadErrorSlot(const QString& error);
signals:/** @fileChangedSignal,文件改变信号* @param1,文件改变动作,动作包含*	FILE_ACTION_ADDED,*	FILE_ACTION_REMOVED,*	FILE_ACTION_MODIFIED,*	FILE_ACTION_RENAMED_OLD_NAME,*	FILE_ACTION_RENAMED_NEW_NAME* @param2,文件名* @return,void*/void fileChangedSignal(ulong action, const QString& fileName);/** @threadErrorSignal,线程错误信号* @param1,错误信息* @return,void*/void threadErrorSignal(const QString& error);private://路径名列表QStringList m_pathNameList;//监控子路径列表QList m_monitorSubpathList;//最终错误QString m_lastError = "未知错误";//线程列表QList m_threadList;
};/*
* @class WatcherThread,观察者线程
*/
class WatcherThread : public QThread {Q_OBJECT
public:/** @WatcherThread,构造[重载1]* @param1,父对象*/WatcherThread(QObject* parent = nullptr);/** @WatcherThread,构造[重载2]* @param1,路径句柄* @param2,路径名* @param3,是否监控子路径*/WatcherThread(HANDLE handle, const QString& pathName, bool monitorSubpath);/** @~WatcherThread,析构*/~WatcherThread();/** @setPath,设置路径* @param1,路径名* @param2,是否监控子路径* @return,void*/void setPath(const QString& pathName, bool monitorSubpath);/** @setHandle,设置句柄* @param1,句柄* @return,void*/void setHandle(HANDLE handle);/** @startThread,开启线程* @return,void*/void startThread();/** @stopThread,停止线程* @return,void*/void stopThread();protected:/** @run,重写父类QThread::run* @return,void*/virtual void run() override;private://监控路径句柄HANDLE m_handle = INVALID_HANDLE_VALUE;//控制线程退出事件HANDLE m_event = nullptr;//路径名QString m_pathName;//是否监控子路径bool m_monitorSubpath = true;
signals:/** @fileChangedSignal,文件改变信号* @param1,文件改变动作* @param2,文件名* @return,void*/void fileChangedSignal(ulong action, const QString& fileName);/** @threadErrorSignal,线程错误信号* @param1,错误信息* @return,void*/void threadErrorSignal(const QString& error);
};

FileSystemWatcher.cpp 

#include "FileSystemWatcher.h"FileSystemWatcher::FileSystemWatcher(QObject* parent)
{}FileSystemWatcher::FileSystemWatcher(const QStringList& pathNameList, const QList& monitorSubpathList, QObject* parent)
{setMonitorPath(pathNameList, monitorSubpathList);
}FileSystemWatcher::~FileSystemWatcher()
{}const QString& FileSystemWatcher::getLastError()
{return m_lastError;
}void FileSystemWatcher::setMonitorPath(const QStringList& pathNameList, const QList& monitorSubpathList)
{m_pathNameList = pathNameList;for (auto& x : m_pathNameList){const QString& c = x.right(1);if (c != "\\" && c != "/"){x.append("\\");}}m_monitorSubpathList = monitorSubpathList;while (m_monitorSubpathList.size() < m_pathNameList.size()){m_monitorSubpathList.append(true);}
}bool FileSystemWatcher::startMonitor()
{bool result = false, success = true;do{QList handleList;for (const auto& x : m_pathNameList){HANDLE handle = CreateFileW(x.toStdWString().c_str(),GENERIC_READ | GENERIC_WRITE | FILE_LIST_DIRECTORY,FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,nullptr,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,nullptr);if (handle == INVALID_HANDLE_VALUE){setLastError(QString("获取%1路径句柄失败,错误代码%2").arg(x).arg(::GetLastError()));success = false;break;}handleList.append(handle);}if (!success){for (const auto& x : handleList){CloseHandle(x);}break;}for (int i = 0; i < handleList.size(); ++i){WatcherThread* thread = new(std::nothrow) WatcherThread(this);if (!thread){setLastError(QString("创建监控%1线程失败").arg(m_pathNameList[i]));break;}connect(thread, &WatcherThread::fileChangedSignal, this, &FileSystemWatcher::fileChangedSlot);connect(thread, &WatcherThread::threadErrorSignal, this, &FileSystemWatcher::threadErrorSlot);m_threadList.append(thread);connect(thread, &QThread::finished, thread, &QThread::deleteLater);thread->setPath(m_pathNameList[i], m_monitorSubpathList[i]);thread->setHandle(handleList[i]);thread->startThread();}result = true;} while (false);return result;
}void FileSystemWatcher::stopMonitor()
{for (const auto& x : m_threadList){x->stopThread();}m_threadList.clear();
}bool FileSystemWatcher::restartMonitor()
{stopMonitor();return startMonitor();
}const QStringList& FileSystemWatcher::getPathNameList()
{return m_pathNameList;
}const QList& FileSystemWatcher::getMonitorSubpathList()
{return m_monitorSubpathList;
}void FileSystemWatcher::setLastError(const QString& error)
{m_lastError = error;
}void FileSystemWatcher::threadErrorSlot(const QString& error)
{emit threadErrorSignal(error);
}void FileSystemWatcher::fileChangedSlot(ulong action, const QString& fileName)
{emit fileChangedSignal(action, fileName);
}WatcherThread::WatcherThread(QObject* parent):QThread(parent)
{
}WatcherThread::WatcherThread(HANDLE handle, const QString& pathName, bool monitorSubpath)
{setHandle(handle);setPath(pathName, monitorSubpath);
}WatcherThread::~WatcherThread()
{stopThread();if (isRunning()){wait(3000);}
}void WatcherThread::setPath(const QString& pathName, bool monitorSubpath)
{m_pathName = pathName;m_monitorSubpath = m_monitorSubpath;
}void WatcherThread::setHandle(HANDLE handle)
{m_handle = handle;
}void WatcherThread::startThread()
{this->start();
}void WatcherThread::stopThread()
{if (!isRunning())return;CancelIoEx(m_handle, nullptr);SetEvent(m_event);
}void WatcherThread::run()
{uchar buffer[1024] = { 0 };ulong bytes = 0, offset = 0, error = 0;m_event = CreateEventW(nullptr, true, false, nullptr);OVERLAPPED lapped;lapped.hEvent = CreateEventW(nullptr, true, false, nullptr);if (!m_event || !lapped.hEvent){emit threadErrorSignal(QString("创建路径%1事件失败,错误代码%2").arg(m_pathName).arg(::GetLastError()));}else{do{memset(buffer, 0, sizeof(buffer));if (ReadDirectoryChangesW(m_handle, buffer, sizeof(buffer), m_monitorSubpath,FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE,&bytes, &lapped, nullptr)){if (GetOverlappedResult(m_handle, &lapped, &bytes, TRUE)){FILE_NOTIFY_INFORMATION* info = reinterpret_cast(buffer);do{QString fileName = m_pathName + QString::fromWCharArray(info->FileName, info->FileNameLength / sizeof(wchar_t));emit fileChangedSignal(info->Action, fileName);offset = info->NextEntryOffset;info = reinterpret_cast(reinterpret_cast(info) + offset);} while (offset);}else{break;}if (!ResetEvent(lapped.hEvent)){break;}}else{emit threadErrorSignal(QString("监控路径%1文件变化失败,错误代码%2").arg(m_pathName).arg(::GetLastError()));}error = WaitForSingleObject(m_event, 0);} while (error == WAIT_TIMEOUT);CloseHandle(lapped.hEvent);CloseHandle(m_handle);CloseHandle(m_event);}quit();
}

参考网址:异步readdirectorychangesw调用阻止线程退出 - IT屋-程序员软件开发技术分享社区 

不建议使用该类库,推荐文件监控类库:

C/C++文件监控_夜晚不懂天的白的博客-CSDN博客 


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部