【文件监控】之一:理解 ReadDirectoryChangesW part1
理解 ReadDirectoryChangesW
- 原作者:Jim Beveridge
- 原文:http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html?amp
- 渣翻译:bbcallen@gmail.com
世界上最长,最详细的 ReadDirectoryChangesW 的使用方法描述。
下载本文的示例代码
之前,我花了一周时间研究文档少得可怜的 ReadDirectoryChangesW。希望这篇文章可以为大家节约一些时间。我相信我已经读过了我能找到所有相关文章和大量代码。几乎所有的例子,包括微软自己的那个例子,都有明显缺陷或低级错误。
我曾在《Multithreading Applications in Win32》这本书中的某一章,介绍了同步IO,激发态内核对象,重叠IO,IO完成端口的区别。现在要谈的这个问题,对我来说是小菜一碟。只不过上次写 重叠IO的痛苦折磨了我好多年,这次应该也不会例外。
监控文件和目录的四种方式
在 Windows Vista 中,SHChangeNotifyRegister 已经可以报告所有文件的所有变更。但问题是,还存在上亿不打算立即升级的 Windows XP 用户。
注意,FindFirstChangeNotification 和 ReadDirectoryChangesW 是互斥的,不能同时使用。
Windows XP 引入了最终解决方案,变更日志(Change Journal)可以跟踪每一个变更的细节,即使你的软件没有运行。很帅的技术,但也相当难用。
对我来说,使用 ReadDirectoryChangesW,在性能和复杂度上会是一个很好的平衡。
谜题
- A. I/O模式:
- 阻塞同步(Blocking synchronous)
- 触发式同步(Signaled synchronous)
- 重叠异步(Overlapped asynchronous)
- 完成例程(Completion Routine) (又名 Asynchronous Procedure Call or APC)
- B. 当调用 WaitForXxx 函数的时候:
- 等待目录句柄
- 等待 OVERLAPPED 结构体里的 Event 对象
- 什么都不等 (APCs)
- C. 处理通知:
- 阻塞
- WaitForSingleObject
- WaitForMultipleObjects
- WaitForMultipleObjectsEx
- MsgWaitForMultipleObjectsEx
- IO完成端口(I/O Completion Ports)
- D. 线程模型:
- 每个工作线程调用一次 ReadDirectoryChangesW.
- 每个工作线程调用多次 ReadDirectoryChangesW.
- 在主线程上调用多次 ReadDirectoryChangesW.
- 多个线程进行多个调用. (I/O Completion Ports)
最后,当调用 ReadDirectoryChangesW 的时候,你可以通过 flags 选择你要监控的内容,包括文件创建,内容变更,属性变更等等。你可以多次调用,每次一个 flag,也可以在一次调用中使用多个 flag。多个 flag 总是正确的解决方案。但如果你为了调试方便,需要一个 flag 一个 flag 的调用的话,那就需要从 ReadDirectoryChangesW 返回的通知缓冲区中读取更多的数据。
如果你的脑子正在囧的话,那么你就能够明白为什么那么多人都没法搞定这件事了。
建议的解决方案
那么正确的答案是什么呢?我的建议是:取决于你认为最重要的是什么。
错误的技术
在研究解决方案的时候,我见识过各种用法:不靠谱的,错误的,以及错得离谱的。
如果你有上千个目录需要监控,不要使用简单方案。转换为平衡方案。或者监控公共的根目录,并忽略不关心的文件。
获取目录句柄
HANDLE hDir = ::CreateFile( strDirectory, // 文件名的指针 FILE_LIST_DIRECTORY, // 访问(读/写)模式 FILE_SHARE_READ // 共享模式 | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, // security descriptor OPEN_EXISTING, // 如何创建 FILE_FLAG_BACKUP_SEMANTICS // 文件属性 | FILE_FLAG_OVERLAPPED, NULL); // 文件属性的模板文件
共享模式也存在一个陷阱,我看到一些例子没有使用 FILE_SHARE_DELETE。也许你认为目录不会被删除,所以没有问题。但是,这回导致其他进程无法重命名或者删除这个目录下的文件
这个函数另一个潜在的陷阱在于,被引用的目录本身处于”使用中“的状态,并且无法被删除。如果希望在监控目录的同时,还允许目录被删除,你应当监控该目录的父目录及父目录下的文件和子目录。
调用 ReadDirectoryChangesW
实际调用 ReadDirectoryChangesW 是整个操作中最简单的环节。如果你使用了完成例程,唯一需要注意的就是缓冲区必须是DWORD对齐的。
void CChangeHandler::BeginRead() { ::ZeroMemory(&m_Overlapped, sizeof(m_Overlapped)); m_Overlapped.hEvent = this; DWORD dwBytes=0; BOOL success = ::ReadDirectoryChangesW( m_hDirectory, &m_Buffer[0], m_Buffer.size(), FALSE, // monitor children? FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_FILE_NAME, &dwBytes, &m_Overlapped, &NotificationCompletion); }
由于我们使用了重叠I/O,m_Buffer直到完成例程被调用的时候才会填充。
分派完成例程
处理通知
通知例程很简单,只要读取数据并保存就可以了。真的是这样么?错。完成例程的实现也有其复杂度。
你可以在单次调用中接收多个处理。务必遍历数据结构,并挨个检查非空的 NextEntryOffset 字段
FILE_NOTIFY_INFORMATION* fni = (FILE_NOTIFY_INFORMATION*)buf; CStringW wstr(fni.Data, fni.Length / sizeof(wchar_t));
有一点我不太清除,那就是在完成例程被调用和发起新的 ReadDirectoryChangesW 调用之间,变更通知做了什么事情。
Using the Notifications
另一方面,如果你使用Windows Vista引入的符号链接,被链接的文件不会生成通知。仔细想想,也说得过去,但是你得小心各种各样的可能性。
还有第三种可能,就是 Junction 从一个分区链接到另一个。这种情况下,对子目录的监控不会监控被链接分区中的文件。这种行为也说得通,但是当发生在用户的机器上时,这种现象会令人感到困惑。
关停
于是我搜索了各种各样的关于调用 CancelIo 的网页,我找到这个网页 中包含这样的代码:
CancelIo(pMonitor->hDir); if (!HasOverlappedIoCompleted(&pMonitor->ol)) { SleepEx(5, TRUE); } CloseHandle(pMonitor->ol.hEvent); CloseHandle(pMonitor->hDir); CancelIo(pMonitor->hDir);
这个看起来很有希望成功,我信心满满得把这段代码拷贝到我的程序中,但是不管用。
我最终的解决方案是跟踪未完成的请求数目,然后持续调用 SleepEx 直到计数为0,在示例代码中,关停的顺序如下:
- 程序调用 CReadDirectoryChanges::Terminate (或者简单的析构对象)
- Terminate 通过 QueueUserAPC 发送消息到工作线程中的 CReadChangesServer,通知其结束。
- CReadChangesServer::RequestTermination 将 m_bTerminate 设置为 true,然后将调用转发给 CReadChangesRequest 对象,每个对象对自己的目录句柄调用 CancelIo 然后关闭目录句柄。
- 控制返回到 CReadChangesServer::Run 函数,注意这时还没有任何东西实际结束。
void Run() { while (m_nOutstandingRequests || !m_bTerminate) { DWORD rc = ::SleepEx(INFINITE, true); } }
- CancelIo 导致 Windows 自动对每一个 CReadChangesRequest 重叠请求调用完成例程。每个调用的 dwErrorCode 都被设置为 ERROR_OPERATION_ABORTED。
- 完成例程删除 CReadChangesRequest 对象,减少 nOutstandingRequests 计数,然后在不发起新请求的情况下返回。
- 由于一个或多个APCs完成,SleepEx返回。nOutstandingRequests 为0,bTerminate 为true,于是函数退出,线程被干净的结束。
万一关停没有被合适的处理,主线程会根据一个超时时间等待工作线程结束。如果工作线程没有顺利结束,我们就让 Windows 结束时干掉它。
网络驱动器
总结
转载于:https://www.cnblogs.com/h2zZhou/p/9237091.html
【文件监控】之一:理解 ReadDirectoryChangesW part1相关推荐
- VC++文件监控(一) ReadDirectoryChangesW
From: http://www.cnblogs.com/doublesnke/archive/2011/08/16/2141374.html VC++实施文件监控:实例和详解 相关帮助: http: ...
- 理解 ReadDirectoryChangesW
理解 ReadDirectoryChangesW 原作者:Jim Beveridge 原文:http://qualapps.blogspot.com/2010/05/understanding-rea ...
- 文件监控(二) 代码
文件监控(二) 代码 ,目前监控只支持WINDOWS //①开始监控目录 // 将监控到的文件放入list void* ThreadWatcher(void* pParam) { dzlog_not ...
- linux服务器监控谁改了文件,linux服务器文件监控
linux服务器文件监控 内容精选 换一换 为了防止您的主机被勒索病毒侵害,请创建防护策略,将重点防御的文件添加到防护策略的监控路径中,并启动机器学习.机器学习会自动聚类并收集该策略下的所有服务器的正 ...
- zabbix之日志文件监控
一.日志item介绍 下面介绍zabbix另一个"重量级"的功能--日志文件监控,它最主要的是监控日志文件中有没有某个字符串的表达式,对应日志轮转与否,zabbix都支持. 在配 ...
- zabbix监控linux文件目录,zabbix之日志文件监控
一.日志item介绍 下面介绍zabbix另一个"重量级"的功能--日志文件监控,它最主要的是监控日志文件中有没有某个字符串的表达式,对应日志轮转与否,zabbix都支持. 在配置 ...
- python watchdog 同时检测到多个事件_python中watchdog文件监控与检测上传功能
引言 上一篇介绍完了观察者模式的原理,本篇想就此再介绍一个小应用,虽然我也就玩了一下午,是当时看observer正好找到的,以及还有Django-observer,但Django很久没用了,所以提下这 ...
- JAVA 文件监控 WatchService
概述 java1.7中 提供了WatchService来监控系统中文件的变化.该监控是基于操作系统的文件系统监控器,可以监控系统是所有文件的变化,这种监控是无需遍历.无需比较的,是一种基于信号收发的监 ...
- C#之操作窗口模拟键鼠事件文件监控等知识使用
C#之操作窗口模拟键鼠事件文件监控等知识使用 实现的效果图片,这里我在输入框输入一个号码,在C盘下创建一个文件txt文件, 被filewatch监控到,里面往指定窗口送sn被输入一些键盘鼠标的动作等操 ...
最新文章
- 20145240《信息安全系统设计基础》第十二周学习总结
- ClickOnce部署(3):使用证书
- 简单而不简陋﹣wp7视觉点滴
- express中获取url参数
- python对json的相关操作
- H3CSE园区-VLAN配置
- 如何轻松查询分析多个快递单号物流到站派件延误件
- 监理项目的服务器,idc服务器机房搬迁工程施工监理的重点
- Linux系统中文件颜色分别代表什么?
- JNCIS-FWV Study Guide v1.3
- chrome插件安装方法教程
- 手写Android热修复
- latex 跳转标签_在 LaTeX 中使用交叉引用
- [Chromium中文文档]Chrom{e,ium}{,OS}中的硬件视频加速
- [转]deepin系统添加开机运行命令、软件自启动方法
- Opencv出现错误 cv2.error:OpenCV(4.4.0)C:\Users\appveyor\AppData\Local\Temp\1\pip-req-build-6lylwdcz\open
- 18 副为程序员定制的对联,总有一副适合你...流泪
- 用MATLAB实现m序列的生成(MATLAB 2021a适用)
- 新手摆摊进来看看(明确卖什么很关键)
- 板凳——————————————————(昏鸦)Introduction to Java Programming