前言
目录监视器主要的作用是可以监视一个目录下的文件变化,网上查到的有三种方法:
- 使用
FindFirstChangeNotification等系列函数使用,缺点很明显,只能检测目录下的文件是否发生变化,却不知道是哪个文件发生了变化,过于简陋,就不予考虑了。 - 使用
ReadDirectoryChangesW函数,该函数就要比上面那种详细一点,会返回具体发生变化的文件名称,但如果检测目录发生大量变化,很可能会遗漏某些文件变化事件,且必须软件持续运行才行。 - 使用
change journals,该方式当然是最好的,甚至可以无需软件持续运行,但同样它也是最为复杂的,大名鼎鼎的everything似乎就是用的这种方法
综合考虑之下,选择第二种方式来实现一个目录监视器,信息较为全面,且使用也较为简单
函数介绍
可直接点击官方文档查看
BOOL ReadDirectoryChangesW(
HANDLE hDirectory, //要进行监视的目录句柄,使用createfile函数得到
LPVOID lpBuffer, //存放发生了改变的文件信息
DWORD nBufferLength, //缓存区的大小
BOOL bWatchSubtree, //是否监视子目录
DWORD dwNotifyFilter, //过滤要进行监视的事件
LPDWORD lpBytesReturned, //返回接收到的字节数
LPOVERLAPPED lpOverlapped, //重叠结构,用于异步操作
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine //例程函数,一般不使用,填NULL
);
该函数的作用就是监视某一个目录,当以同步方式调用时(即lpOverlapped为NULL),该函数会阻塞,直到文件夹内发生了任何变化,将结果返回值缓存区lpBuffer中,用FILE_NOTIFY_INFORMATION结构取得相应信息。
typedef struct _FILE_NOTIFY_INFORMATION {
DWORD NextEntryOffset; //取得下一个信息需要偏移的字节数,最后一个时,该值为0
DWORD Action; //发生的行为
DWORD FileNameLength; //发生改变的文件名长度,按字节计数
WCHAR FileName[1]; //文件名
} FILE_NOTIFY_INFORMATION, *PFILE_NOTIFY_INFORMATION;
基本使用方法
打开要进行监视的文件夹
HANDLE m_hFile = CreateFileW(L“D:\\ps”, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE| FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
注意,第二个参数必须有FILE_LIST_DIRECTORY,以及倒数第二个参数必须有FILE_FLAG_BACKUP_SEMANTICS,否则失败。
开始监视
DWORD lbyte; //得到返回的字节数
char buf[4096];
BOOL ret = ReadDirectoryChangesW(m_hFile, m_buf, 4096, true, FILE_NOTIFY_CHANGE_FILE_NAME, &lbyte, NULL, NULL); //阻塞监视
当函数返回时,取得结果
FILE_NOTIFY_INFORMATION* f = (FILE_NOTIFY_INFORMATION*)m_buf; //得到FILE_NOTIFY_INFORMATION结构体,进行解析
//进入循环,解析所有信息
do {
wchar_t* fileName = new wchar_t[f->FileNameLength] {};
memcpy(fileName, f->FileName, f->FileNameLength);
//fileInfo.push_back({ fileName,f->Action }); //这里fileName即为得到的文件名称,f->Action则为该文件发生的行为
delete[] fileName;
f = (FILE_NOTIFY_INFORMATION*)((char*)f + f->NextEntryOffset);
} while (f->NextEntryOffset);
封装为类
class WatchFolder {
HANDLE m_hFile; //进行监视的文件句柄
char* m_buf; //保存文件信息更改的缓存区
public:
struct FILE_INFO
{
std::wstring name;
DWORD action;
FILE_INFO(const wchar_t* fileName, DWORD act) {
name = fileName;
action = act;
}
};
//可监视的属性
enum {
NOTIFY_FILE_NAME = FILE_NOTIFY_CHANGE_FILE_NAME, //监视文件名更改
NOTIFY_DIR_NAME = FILE_NOTIFY_CHANGE_DIR_NAME, //监视目录名更改
NOTIFY_ATTRIBUTES = FILE_NOTIFY_CHANGE_ATTRIBUTES, //监视文件属性更改
NOTIFY_SIZE = FILE_NOTIFY_CHANGE_SIZE, //监视文件大小更改
NOTIFY_LAST_WRITE = FILE_NOTIFY_CHANGE_LAST_WRITE, //监视文件最后写入时间更改
NOTIFY_LAST_ACCESS = FILE_NOTIFY_CHANGE_LAST_ACCESS, //监视文件最后访问时间更改
NOTIFY_CREATION = FILE_NOTIFY_CHANGE_CREATION, //监视文件创建
NOTIFY_SECURITY = FILE_NOTIFY_CHANGE_SECURITY, //监视文件安全描述符更改
NOTIFY_ALL = NOTIFY_DIR_NAME|NOTIFY_FILE_NAME|NOTIFY_SIZE| NOTIFY_LAST_WRITE| NOTIFY_LAST_ACCESS| NOTIFY_CREATION| NOTIFY_ATTRIBUTES| NOTIFY_SECURITY //监视所有情况
};
//发生的行为
enum {
ACTION_ADD = FILE_ACTION_ADDED, //文件添加
ACTION_REMOVE = FILE_ACTION_REMOVED, //文件删除
ACTION_MODIFIED = FILE_ACTION_MODIFIED, //文件更改
ACTION_RENAME_OLD=FILE_ACTION_RENAMED_OLD_NAME, //文件重命名(旧名字)
ACTION_RENAME_NEW=FILE_ACTION_RENAMED_NEW_NAME //文件重命名(新名字)
};
public:
WatchFolder(int bufSize=4096) {
m_hFile = nullptr;
m_buf = new char[bufSize];
}
~WatchFolder()
{
Close();
delete[] m_buf;
}
/**
* @brief 初始化要进行监视的文件
* @param dir
* @return
*/
bool Init(const std::wstring& dir) {
if (m_hFile != nullptr) {
CloseHandle(m_hFile);
m_hFile = nullptr;
}
//打开文件
m_hFile = CreateFileW(dir.data(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE| FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
return m_hFile != nullptr;
}
/**
* @brief 关闭打开的文件句柄
*/
void Close() {
if(m_hFile!=NULL) CloseHandle(m_hFile);
}
/**
* @brief 开始监视文件夹
该实验为 VIP 专享内容,
请开通后继续阅读后续 70% 并运行完整示例~
请开通后继续阅读后续 70% 并运行完整示例~
DISCUSSION
评论 0
还没有评论,来留下第一条吧。