文件监控

最近一个项目,客户要求对某目录进行监控。于是准备写一个小监控程序。实现目录监控一般有三个方案:

1.使用FindFirstChangeNotification,FindNextChangeNotificationFindCloseChangeNotification系列函数。

2.使用未公开函数(老黄历了,早已公开)SHGetSpecialFolderLocation, SHChangeNotifyRegisterSHChangeNotifyDeregister系列函数。

3.使用ReadDirectoryChangesW函数。

第一中方式只能监控到文件夹内文件有变化,具体信息比如那个文件进行了什么操作则无法获得,放弃。

第二种,老强大了,它是将窗口注册到系统的相关观察链上,所以一般操作都逃不过它的监视。不过没有仔细研究,粗看没发现他如何真对具体文件夹实现监控,也许需要自己过滤。

第三种,还有三种使用方式1.使用事件通知。2.使用完成端口通知。3.使用完成实例。

最后决定使用完成端口方式,原因嘛,这个比较高大上,呵呵。其实最主要的原因还是因为完成端口效率高,还有就是实现此要求不高的小功能时代码清晰简洁,当然还有个最主要的原因是百度上有前辈现成的代码 :P。我做了一点调整和修改,将啰嗦多余的东西去掉。调试的过程中还发现个问题。就是如果监视的是网络路径,比如共享文件夹,系统无法进行监视,GetLastError 87 - 参数错误。错误发生在调用 GetQueuedCompletionStatus 之后。百度无果,google打不开网站。求教MSDN,发现在 ReadDirectoryChangesW函数说明的Remark中有这样一句话:

ReadDirectoryChangesW fails with ERROR_INVALID_PARAMETER when the buffer length is greater than 64 KB and the application is monitoring a directory over the network. This is due to a packet size limitation with the underlying file sharing protocols: Server Message Block (SMB) on Windows Me/98/95, and Common Internet File System (CIFS) on Windows Server 2003/XP/2000.

调整大小后问题解决。

下面是代码,为了表示对前辈的尊重,这里贴出前辈的原始代码,只在关键部位做注释。

unit uDirectoryMonitor;
//==============================================================================
// Unit Name: uDirectoryMonitor
// Author : ysai
// Date : 2006-11-28
// Purpose : 目录监视器
// History :
//==============================================================================

interface

uses
Windows, Messages, SysUtils, Classes;

const
NotificationBuffer_Size = 1024 * 1024;  //注意这里就是罪魁祸首,超过了64Key系统限制,改为 1024 * 64。
                            
type
TFileAction = (
faAdded = 1,
faRemoved,
faModifyed,
faRenamedOldName,
faRenamedNewName
);
TMonitorType = (mtDirName, mtAttrib, mtSize, mtSecurity, mtCreationDate,
mtLastAccess, mtLastWrite, mtFileName);
TMonitorTypes = set of TMonitorType;
TDirectoryMonitorChangeEvent =
procedure(Sender: TObject; FileAction : TFileAction; FileName : string) of object;

TDirectoryMonitor = class(TComponent)
private
FDirectoryHandle: THandle;
FNotificationBuffer: array[0..NotificationBuffer_Size] of Byte; //注意这里改成 [0..NotificationBuffer_Size - 1]
FWatchThread: TThread;
FNotifyFilter: DWORD;
FOverlapped: TOverlapped;
FPOverlapped: POverlapped;
FBytesWritten: DWORD;
FCompletionPort: THandle;
FActive: Boolean;
FWatchSubTree: Boolean;
FMonitorDirName: Boolean;
FMonitorAttributes: Boolean;
FMonitorSize: Boolean;
FMonitorSecurity: Boolean;
FMonitorCreationDate: Boolean;
FMonitorLastAccess: Boolean;
FMonitorLastWrite: Boolean;
FMonitorFileName: Boolean;
FDirectory: string;
FOnChange: TDirectoryMonitorChangeEvent;
private
function GetMonitorTypes: TMonitorTypes;
procedure SetMonitorTypes(const Value: TMonitorTypes);
protected
property WatchThread : TThread read FWatchThread;

procedure DoChange(
const FileAction : TFileAction;
const FileName : string
);
procedure Start;
procedure Stop;
procedure SetActive(const Value: Boolean);
public
property Active : Boolean read FActive Write SetActive;

constructor Create(AOwner: TComponent);override;
destructor Destroy; override;
published
property Directory : string read FDirectory Write FDirectory;

property WatchSubTree : Boolean read FWatchSubTree Write FWatchSubTree default False;

//这些属性,个人认为比较啰嗦,与下面的 MonitorTypes 属性重复,可以去掉。
property MonitorFileName : Boolean read FMonitorFileName Write FMonitorFileName default False;
property MonitorDirName : Boolean read FMonitorDirName Write FMonitorDirName default False;
property MonitorAttributes : Boolean read FMonitorAttributes Write FMonitorAttributes default False;
property MonitorSize : Boolean read FMonitorSize Write FMonitorSize default False;
property MonitorLastWrite : Boolean read FMonitorLastWrite Write FMonitorLastWrite default False;
property MonitorLastAccess : Boolean read FMonitorLastAccess Write FMonitorLastAccess default False;
property MonitorCreationDate : Boolean read FMonitorCreationDate Write FMonitorCreationDate default False;
property MonitorSecurity : Boolean read FMonitorSecurity Write FMonitorSecurity default False;

property MonitorTypes : TMonitorTypes read GetMonitorTypes write SetMonitorTypes;

property OnChange : TDirectoryMonitorChangeEvent read FOnChange Write FOnChange;
end;

procedure Register;

implementation

type
PFileNotifyInformation = ^TFileNotifyInformation;
TFileNotifyInformation = record
NextEntryOffset: DWORD;
Action: DWORD;
FileNameLength: DWORD;
FileName: array[0..0] of WideChar;
end;

TWaitThread = class(TThread)
private
FOwner : TDirectoryMonitor;
procedure HandleEvent;
protected
procedure Execute; override;
public
constructor Create(Owner: TDirectoryMonitor);
end;

const
FILE_LIST_DIRECTORY = $0001;

{ TDirectoryMonitor }

constructor TDirectoryMonitor.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FCompletionPort := 0;
FDirectoryHandle := 0;
FPOverlapped := @FOverlapped;
ZeroMemory(@FOverlapped, SizeOf(FOverlapped));
end;

destructor TDirectoryMonitor.Destroy;
begin
Active := False;
inherited;
end;

procedure TDirectoryMonitor.DoChange(
const FileAction : TFileAction;
const FileName : string
);
var
fname : string;
begin
if Pos(‘:‘, FileName) > 0 then
fname := FileName
else
if FDirectory[Length(FDirectory)] = ‘\‘ then
fname := FDirectory + FileName
else
fname := FDirectory + ‘\‘ + FileName;
if Assigned(FOnChange) then
FOnChange(Self, FileAction, fname);
end;

function TDirectoryMonitor.GetMonitorTypes: TMonitorTypes;
begin
result := [];
if FMonitorAttributes then result := result + [mtAttrib];
if FMonitorCreationDate then result := result + [mtCreationDate];
if FMonitorDirName then result := result + [mtDirName];
if FMonitorFileName then result := result + [mtFileName];
if FMonitorLastAccess then result := result + [mtLastAccess];
if FMonitorLastWrite then result := result + [mtLastWrite];
if FMonitorSecurity then result := result + [mtSecurity];
if FMonitorSize then result := result + [mtSize];
end;

procedure TDirectoryMonitor.SetMonitorTypes(const Value: TMonitorTypes);
begin
MonitorAttributes := mtAttrib in Value;
MonitorCreationDate := mtCreationDate in Value;
MonitorDirName := mtDirName in Value;
MonitorFileName := mtFileName in Value;
MonitorLastAccess := mtLastAccess in Value;
MonitorLastWrite := mtLastWrite in Value;
MonitorSecurity := mtSecurity in Value;
MonitorSize := mtSize in Value;
end;

procedure TDirectoryMonitor.SetActive(const Value: Boolean);
begin
if Value <> FActive then
begin
if Value then
Start
else
Stop;
FActive := Value;
end;
end;

procedure TDirectoryMonitor.Start;
begin
FNotifyFilter := 0;
if MonitorFileName then
FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_FILE_NAME;
if MonitorDirName then
FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_DIR_NAME;
if MonitorAttributes then
FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_ATTRIBUTES;
if MonitorSize then
FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_SIZE;
if MonitorLastWrite then
FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_LAST_WRITE;
if MonitorLastAccess then
FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_LAST_ACCESS;
if MonitorCreationDate then
FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_CREATION;
if MonitorSecurity then
FNotifyFilter := FNotifyFilter or FILE_NOTIFY_CHANGE_SECURITY;

if FNotifyFilter = 0 then
begin
raise Exception.Create(‘请选择监视内容!‘);
end;

FDirectoryHandle := CreateFile(
PChar(FDirectory),
FILE_LIST_DIRECTORY,
FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
nil,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OVERLAPPED,
0);
if FDirectoryHandle = INVALID_HANDLE_VALUE then
begin
FDirectoryHandle := 0;
raise Exception.Create(SysErrorMessage(GetLastError));
end;
FCompletionPort := CreateIoCompletionPort(FDirectoryHandle, 0, Longint(pointer(self)), 0);
ZeroMemory(@FNotificationBuffer, SizeOf(FNotificationBuffer));
FBytesWritten := 0;
if not ReadDirectoryChanges(
FDirectoryHandle,
@FNotificationBuffer,
SizeOf(FNotificationBuffer),
WatchSubTree,
FNotifyFilter,
@FBytesWritten,
@FOverlapped,
nil
) then
begin
CloseHandle(FDirectoryHandle);
FDirectoryHandle := 0;
CloseHandle(FCompletionPort);
FCompletionPort := 0;
raise Exception.Create(SysErrorMessage(GetLastError));
end;
FWatchThread := TWaitThread.Create(Self);
{$IFDEF DELPHI2009_UP}
TWaitThread(FWatchThread).Start;
{$ELSE}
TWaitThread(FWatchThread).Resume;
{$ENDIF}
end;

procedure TDirectoryMonitor.Stop;
begin
if FCompletionPort = 0 then
exit;
PostQueuedCompletionStatus(FCompletionPort, 0, 0, nil);
FWatchThread.WaitFor;
FWatchThread.Free;
FWatchThread := nil;
CloseHandle(FDirectoryHandle);
FDirectoryHandle := 0;
CloseHandle(FCompletionPort);
FCompletionPort := 0;
end;

{ TWaitThread }

constructor TWaitThread.Create(Owner: TDirectoryMonitor);
begin
inherited Create(True);
FOwner := Owner;
FreeOnTerminate := False;
end;

procedure TWaitThread.Execute;
var
numBytes: DWORD;
//cbOffset: DWORD;
CompletionKey: DWORD;
begin
while not Terminated do
begin
GetQueuedCompletionStatus(FOwner.FCompletionPort, numBytes, CompletionKey, FOwner.FPOverlapped, INFINITE);
if CompletionKey <> 0 then
begin
Synchronize(HandleEvent);
FOwner.FBytesWritten := 0;
ZeroMemory(@FOwner.FNotificationBuffer, SizeOf(FOwner.FNotificationBuffer));
ReadDirectoryChanges(
FOwner.FDirectoryHandle,
@FOwner.FNotificationBuffer,
SizeOf(FOwner.FNotificationBuffer),
FOwner.WatchSubTree,
FOwner.FNotifyFilter,
@FOwner.FBytesWritten,
@FOwner.FOverlapped,
nil
);
end
else
Terminate;
end;
end;

procedure TWaitThread.HandleEvent;
var
FileOpNotification: PFileNotifyInformation;
Offset: Longint;
begin
Pointer(FileOpNotification) := @FOwner.FNotificationBuffer[0];
repeat
Offset := FileOpNotification^.NextEntryOffset;
FOwner.DoChange(
TFileAction(FileOpNotification^.Action),
WideCharLenToString(@(FileOpNotification^.FileName), FileOpNotification^.FileNameLength div 2)
);
Pointer(FileOpNotification) := Pointer(Integer(FileOpNotification) + Offset);
until Offset = 0;
end;

procedure Register;
begin
RegisterComponents(‘System‘, [TDirectoryMonitor]);
end;

end.

时间: 2024-08-04 22:29:35

文件监控的相关文章

Android文件监控FileObserver介绍

在前面的Linux文件系统Inotify机制中介绍了Linux对文件变更监控过程.Android系统在此基础上封装了一个FileObserver类来方便使用Inotify机制.FileObserver是一个抽象类,需要定义子类实现该类的onEvent抽象方法,当被监控的文件或者目录发生变更事件时,将回调FileObserver的onEvent()函数来处理文件或目录的变更事件. 事件监控过程 在FileObserver类中定义了一个静态内部类ObserverThread,该线程类才是真正实现文件

在C#使用文件监控对象FileSystemWatcher的几种方案

最近在项目中有这么个需求,就是得去实时获取某个在无规律改变的文本文件中的内容.首先想到的是用程序定期去访问这个文件,因为对实时性要求很高,间隔不能超过1S,而且每次获取到文本内容都要去分发给web服务器做别的操作,而那个文本的写入有时候会频繁,1秒可能多次,但是也有可能在相当长一段时间内是没有任何写入的. 这样一来如果每秒都去访问文件的话,一个是IO问题,还有就是每次操作都会引起后端一系列程序的反应,文本在长时间内无写入的话,一秒一次的触发一系列徒劳的事情太不可取了. 最终发现了c#中的File

zabbix之日志文件监控

一.日志item介绍 下面介绍zabbix另一个"重量级"的功能--日志文件监控,它最主要的是监控日志文件中有没有某个字符串的表达式,对应日志轮转与否,zabbix都支持. 在配置Item的时候,Type选择Zabbix agent (active),这里主要需要配置的是Key.下面是监控日志的两种key--log和logtr. log[/path/to/some/file,<regexp>,<encoding>,<maxlines>,<mod

falcon-web文件监控

1.配置前置环境,下载inotify-tools-3.14,libiconv-1.14源码包,通过yum安装libcurl库文件,另外由于最后编译提示缺少mysql库文件,yum还安装不到,所以我多了一步安装mysql 5.6的步骤. 安装apache环境 # yum install -y httpd 安装inotify-tools # tar xvzf inotify-tools-3.14.tar.gz # cd inotify-tools-3.14 # ./configure # make

基于SSM框架下的JAVA文件监控管理系统

每天记录学习,每天会有好心情.*^_^* 今日思考,完成一个文件监控管理系统项目,需要实现哪些功能?此类项目常见描述如下:随着信息技术突飞猛进,网络传输已经成为信息传递的主要方式,信息极端主义的手段越来越高深莫测,重要信息的监控显得十分必要.针对信息安全隐患问题,本章设计了文件监控系统,来实现对传输的重要文件进行实时监控,达到安全传输文件以防非法窃取的目的.SSM(MYECLIPSE)框架及其适合本类项目,使用MVC的思想可以极大程度减少重复工作量.和SSM(MYECLIPSE)框架最配的开发工

web文件监控系统

每天记录学习,每天会有好心情.*^_^* 今天记录的项目是基于web的文件监控系统,针对信息安全隐患问题,本章设计了文件监控系统,来实现对传输的重要文件进行实时监控,达到安全传输文件以防非法窃取的目的.采用当前非常流行的B/S体系结构,以JAVA作为开发技术,主要依赖SSM技术框架,mysql数据库建立本系统.做基于web的文件监控系统的时候,我们需要用到SSM(MYECLIPSE)框架,开发工具选用最拿手的MYECLIPSE.基于web的文件监控系统项目是一个 后台项目.开发系统时,需求分析是

四步搞定Zabbix 日志文件监控

Zabbix 日志文件监控 一.给运行Zabbix agent的用户授予要监控日志的读取权限. 1. 執行下面的命令,追加app的可讀權限: setfacl -m u:app:r-- /var/log/messages 2. 修改logrotate配置 sed -i '/kill/a\/usr\/bin\/setfacl -m u:app:r-- \/var\/log\/messages' /etc/logrotate.d/syslog 二.在OS Template添加监控Item,主要Type

inotifywait实现文件监控

应用场景文件监控可以配合rsync实现文件自动同步,例如监听某个目录,当文件变化时,使用rsync命令将变化的文件同步.(可用于代码自动发布) 安装noitify下载地址:http://github.com/downloads/rvoicilas/inotify-tools/inotify-tools-3.14.tar.gz cd inotify-tools-3.14./configuremakemake install1234安装成功后在/usr/local/bin 下会有inotifywai

java文件监控[转]

原文链接:http://blog.csdn.net/dancen/article/details/7786987#comments 问题:存在两个文件目录,且称之为源目录和目标目录,需要不定期将源目录和目标目录进行同步.两种同步方法:1 采用从源目录到目标目录的完全拷贝覆盖.显而易见的缺点,当文件目录中文件多.体积大时拷贝过程时间消耗极大.2 采用从源目录到目标目录的变更集拷贝覆盖.避免了大量拷贝的IO耗时操作,但产生了新的问题:如何获取变更信息? 新问题:如何监控一个文件目录的变更情况.还是两

文件监控(教学版)

参考FileSpy写的文件监控程序,但比它的抽象多了.可能瑞星的文件驱动也是这样写的,否则它为什么老阻止我安装驱动呢.测试程序是一个命令行小程序,负责打开设备,开启监控和关闭监控,运行时开启和关闭两次. 在DebugView中查看输出信息,我只是想看看能不能达到目的,所以信息量很少. 在驱动程序中开启和关闭监控的代码: VOID AttachedToDeviceByName (__in PWSTR DeviceName, __in BOOLEAN attach) { UCHAR tmp_buf1