自动检测文件

标签:

杂谈

 
Win32中的目录监控
Windows里面对目录、文件监控提供了两个API,它们分别是:FindFirstChangeNotification和ReadDirectoryChangesW。

1、FindFirstChangeNotification
HANDLE FindFirstChangeNotification(
LPCTSTR lpPathName,
BOOL bWatchSubtree,
DWORD dwNotifyFilter
);
该API能够监控文件名、目录名、文件属性、子文件夹、文件大小、文件的最后写时间、安全属性的改变。该函数返回的是一个监控句柄 (Notification Handle),该句柄能够被 WaitForMultipleObjects 其上进行等待,当该句柄所监控的条件满足时,该句柄就会处于激发状态,这时用户程序就能够知道该目录发生了变化。在进行了用户自己的处理之后,一定要调用 BOOL FindNextChangeNotification(HANDLE hChangeHandle)来将该监控句柄置为去激活状态,并继续等待下一次被激活。如果你不再想监控了,就要调用 FindCloseChangeNotification关闭监控句柄。


2、ReadDirectoryChangesW

CODE:.DLL命令文件夹监控, 逻辑型, , "ReadDirectoryChangesW"
    .参数 目录句柄, 整数型, , 通过获取目录命令或得的句柄
    .参数 返回信息指针, FILE_NOTIFY_INFORMATION, 传址
    .参数 内存长度, 整数型
    .参数 监控类型, 逻辑型
    .参数 过滤器, 整数型
    .参数 内存大小, 整数型
    .参数 异步使用, OVERLAPPED, 传址, 一个OVERLAPPED结构指针,给异使用,其他为NULL
    .参数 操作状态, 整数型, , 完成或取消时,指针指向完成程序
这个函数的参数很多,但是在某种特定的使用场合下,通常不会用到全部的参数,也就前面的5个。

该函数提供了同步和异步的两大类方法,异步方法里面又有三种不同异步处理机制。(注意:这里所说的同步或异步指的是操作系统提供的同步或异步机制,在用户程序看来使用了操作系统同步机制的代码仍然可以是异步的。)

先来看看第一个参数hDirectory,从字面上看来是一个目录句柄,其实是一个文件句柄,在操作系统的眼里目录也只是一种特殊的文件,因此该句柄的获得需要调用CreateFile函数:

CODE:.DLL命令获取目录句柄, 整数型, "kernel32", "CreateFileA", , 用来返回目录句柄
    .参数 目录地址, 文本型
    .参数 访问权限, 整数型, , 1+1073741824,读访问和写访问
    .参数 共享权限, 整数型
    .参数 安全特性, 整数型
    .参数 文件特性, 整数型, , 文件一定要存在
    .参数 标记属性, 整数型, , 标记属性,允许您去开启一个目录,并允许您改变目录的属性
    .参数 复制属性, 整数型
知道了目录句柄如何获得的,就可以看看同步方式如何监控目录了。

首先我们浏览一下其他的参数,从最简单的布尔变量看起,bWatchSubtree一眼就能够看出是:是否监控子目录。OK,再看DWORD dwNotifyFilter这个参数肯定是目录变化通知过滤器了,查看MSDN文档知道修改文件名的过滤器选项就是 FILE_NOTIFY_CHANGE_FILE_NAME,修改文件就是FILE_NOTIFY_CHANGE_LAST_WRITE,等等。

然后要了解其他的参数有何意义就要先了解一下操作系统把目录变化通知是怎样交给用户代码的。通知是放在哪里?有些什么内容?它的内存是由谁分配的?如果是由用户分配的(这是最自然的一种想法了),又是怎样告知操作系统的?好了,想到这里,我想第二、第三个参数的意义已经很明确了,用户代码就是通过这两个参数来告知操作系统该把目录变化通知放在首地址为lpBuffer,长度为nBufferLength的一块内存当中的。但是该内存又是怎样组织的呢?操作系统是把他们放在FILE_NOTIFY_INFORMATION 这个结构里面的:

CODE:.数据类型 FILE_NOTIFY_INFORMATION
    .成员 NextEntryOffset, 整数型
    .成员 Action, 整数型
    .成员 FileNameLength, 整数型
    .成员 FileName, 字节型, , "1024"
有经验的人可以很快看出这其实是一个链表结构,第一个字段存储了要获得下一个记录需要跳过多少字节数,如果它的值为0,就表示本记录已经是链表中的最后一条记录了。该字段其实也可以看做是一个指向下一条记录的指针。第二个字段的含义是:本次通知了哪种类型的目录变化。第三个字段表示的是变化的文件或者目录的名称的长度。第四个字段是一个Unicode字符数组的首地址。

哦,知道了目录变化通知是放在一个链表里面的,而且知道这个链表的首地址是lpBuffer,那么这个链表总共有多大呢?这个数值是放在LPDWORD lpBytesReturned参数里面的。
好了,同步监控所涉及到的参数就这么多了,那同步监控的具体步骤呢?很简单:

a)     使用CreateFile获取目录句柄
b)      在一个循环里面调用ReadDirectoryChangesW,并且把自己分配的内存首地址、内存长度、目录句柄传给该函数。用户代码在该函数的调用中进行同步等待。
这样一旦有了目录变化,操作系统便会从对ReadDirectoryChangesW的调用中返回,并把目录变化通知放在了用户指定的位置。

CODE:
.版本 2
.支持库 EThread
.支持库 iext
.程序集 窗口程序集1
.程序集变量 目录句柄, 整数型
.程序集变量 返回信息指针, FILE_NOTIFY_INFORMATION
.程序集变量 重叠操作, OVERLAPPED
.程序集变量 循环, 逻辑型
.程序集变量 目录路线, 文本型
.子程序 _按钮1_被单击
目录路线 = 编辑框2.内容
.如果真 (取文本右边 (目录路线, 1) ≠ “/” 且 目录路线 ≠ “”)
    目录路线 = 编辑框2.内容 + “/”
.如果真结束
.如果 (按钮1.标题 = “启动监视”)
    目录句柄 = 获取目录句柄 (目录路线, #GENERIC_READ + #GENERIC_WRITE, 7, 0, #OPEN_EXISTING, #FILE_FLAG_BACKUP_SEMANTICS, 0)
    .如果 (目录句柄 = #INVALID_HANDLE_VALUE)
        信息框 (“请正确填写监视目录名”, 0, )
        返回 ()
    .否则
    .如果结束
    _启动窗口.标题 = “菜鸟学易——文件夹监控[正在监视中……]”
    编辑框2.禁止 = 真
    编辑框2.背景颜色 = #默认色
    启动线程 (&监控事件, )
    按钮1.标题 = “停止监视”
.否则
    关闭文件 (目录句柄)
    循环 = 真
    按钮1.标题 = “启动监视”
    _启动窗口.标题 = “菜鸟学易——文件夹监控”
    编辑框2.禁止 = 假
    编辑框2.背景颜色 = #白色
.如果结束


.子程序 监控事件
.局部变量 监视结果, 逻辑型
.局部变量 str1, 文本型
.局部变量 str2, 文本型
.局部变量 i, 整数型
.局部变量 消息, 字节集
循环 = 假
.判断循环首 (循环 ≠ 真)
    监视结果 = 文件夹监控 (目录句柄, 返回信息指针, 1024, 真, #FILE_NOTIFY_CHANGE_FILE_NAME + #FILE_NOTIFY_CHANGE_DIR_NAME + #FILE_NOTIFY_CHANGE_ATTRIBUTES + #FILE_NOTIFY_CHANGE_SIZE + #FILE_NOTIFY_CHANGE_LAST_WRITE + #FILE_NOTIFY_CHANGE_LAST_ACCESS + #FILE_NOTIFY_CHANGE_CREATION + #FILE_NOTIFY_CHANGE_SECURITY, 0, 重叠操作, 0)
    .如果 (监视结果)
        ' 输出调试文本 (重叠操作.ternal)
        ' 输出调试文本 (重叠操作.低32位)
        ' 输出调试文本 (重叠操作.高32位)
        ' 输出调试文本 (重叠操作.事件句柄)
        ' 输出调试文本 (重叠操作.ternalHigh)
        消息 = 到字节集 (返回信息指针.FileName)
        .如果 (返回信息指针.NextEntryOffset = 0)
            str1 = Unicode转Ansi (取字节集左边 (消息, 返回信息指针.FileNameLength))
            str2 = “”
        .否则
            str2 = Unicode转Ansi (取字节集左边 (消息, 返回信息指针.FileNameLength))
            str1 = Unicode转Ansi (取字节集中间 (消息, 返回信息指针.NextEntryOffset + 1, 取字节集长度 (消息) -返回信息指针.NextEntryOffset))
        .如果结束
        数组清零 (返回信息指针.FileName)
        .判断开始 (返回信息指针.Action = 2)
            i =超级列表框1.插入表项 (, 到文本 (取现行时间 ()), , , , )
            超级列表框1.置标题 (i, 1, “文件被删除”)
            超级列表框1.置标题 (i, 2, 目录路线 + str1)
            超级列表框1.置标题 (i, 3, 到文本 (返回信息指针.Action))
        .判断 (返回信息指针.Action = 1)
            i =超级列表框1.插入表项 (, 到文本 (取现行时间 ()), , , , )
            超级列表框1.置标题 (i, 1, “文件被创建”)
            超级列表框1.置标题 (i, 2, 目录路线 + str1)
            超级列表框1.置标题 (i, 3, 到文本 (返回信息指针.Action))
        .判断 (返回信息指针.Action = 4 且 str2 ≠ “”)
            i =超级列表框1.插入表项 (, 到文本 (取现行时间 ()), , , , )
            超级列表框1.置标题 (i, 1, “文件被重命名”)
            超级列表框1.置标题 (i, 2, 目录路线 + str2 + “->” + 目录路线 + str1)
            超级列表框1.置标题 (i, 3, 到文本 (返回信息指针.Action))
            str2 = “”
            str1 = “”
        .判断 (返回信息指针.Action = 3 且 str1 ≠ “” 且 str2 = “”)
            i =超级列表框1.插入表项 (, 到文本 (取现行时间 ()), , , , )
            超级列表框1.置标题 (i, 1, “文件被修改”)
            超级列表框1.置标题 (i, 2, 目录路线 + str1)
            超级列表框1.置标题 (i, 3, 到文本 (返回信息指针.Action))
        .默认
            i =超级列表框1.插入表项 (, 到文本 (取现行时间 ()), , , , )
            超级列表框1.置标题 (i, 1, “文件被改变”)
            超级列表框1.置标题 (i, 2, 目录路线 + str1)
            超级列表框1.置标题 (i, 3, 到文本 (返回信息指针.Action))

        .判断结束
        输出调试文本 (返回信息指针.Action)
        输出调试文本 (“=========”)
    .否则
        信息框 (“文件夹监控启动出错,请关闭程序重新运行!”, 0, )
        结束 ()
    .如果结束
.判断循环尾 ()

.子程序 __启动窗口_尺寸被改变
超级列表框1.宽度 = _启动窗口.宽度 - 18
超级列表框1.高度 = _启动窗口.高度 - 90
超级列表框1.置列宽 (0, 超级列表框1.宽度 ÷ 5)
超级列表框1.置列宽 (2, 超级列表框1.宽度 ÷ 1.5)
按钮1.左边 = _启动窗口.宽度 - 207
编辑框2.宽度 = _启动窗口.宽度 - 300

.子程序 Unicode转Ansi, 文本型
.参数 参数_数据, 字节集
.局部变量 局部_计数, 整数型
.局部变量 局部_结果数据, 字节集
.局部变量 局部_结果文本, 文本型
局部_计数 = 取字节集长度 (参数_数据) ÷ 2  ' 字符数目两个
局部_计数 = API_宽字符到双字节1 (936, 512, 参数_数据, 局部_计数, 0, 0, 0, 假)  ' 取转换后的数目
局部_结果数据 = 取空白字节集 (局部_计数)
API_宽字符到双字节 (936, 512, 参数_数据, -1, 局部_结果数据, 局部_计数, 0, 假)
局部_结果文本 = 取字节集数据 (局部_结果数据, #文本型, )
返回 (局部_结果文本)
.子程序 __启动窗口_将被销毁
关闭文件 (目录句柄)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值