zoukankan      html  css  js  c++  java
  • delphi 文件夹操作(监控)

    delphi 监控文件系统

    delphi 监控文件系统 你是否想为你的Windows加上一双眼睛,察看使用者在机器上所做的各种操作(例如建立、删除文件;改变文件或目录名字)呢?

    这里介绍一种利用Windows未公开函数实现这个功能的方法。

    在Windows下有一个未公开函数SHChangeNotifyRegister可以把你的窗口添加到系统的系统消息监视链中,该函数在Delphi中的定义如下:

    Function SHChangeNotifyRegister(hWnd,uFlags,dwEventID,uMSG,cItems:LongWord;
    lpps:PIDLSTRUCT):integer;stdcall;external 'Shell32.dll' index 2;

    其中参数hWnd定义了监视系统操作的窗口得句柄,参数uFlags dwEventID定义监视操作参数,参数uMsg定义操作消息,参数cItems定义附加参数,参数lpps指定一个PIDLSTRUCT结构,该结构指定监视的目录。

    当函数调用成功之后,函数会返回一个监视操作句柄,同时系统就会将hWnd指定的窗口加入到操作监视链中,当有文件操作发生时,系统会向hWnd发送uMsg指定的消息,我们只要在程序中加入该消息的处理函数就可以实现对系统操作的监视了。

    如果要退出程序监视,就要调用另外一个未公开得函数SHChangeNotifyDeregister来取消程序监视。

    下面是使用Delphi编写的具体程序实现范例,首先建立一个新的工程文件,然后在Form1中加入一个Button控件和一个Memo控件,

    程序的代码如下:

    unit Unit1;
    
    interface
    
    uses
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
    Dialogs ,shlobj,Activex, StdCtrls;
    
    const
    SHCNE_RENAMEITEM = $1;
    SHCNE_CREATE = $2;
    SHCNE_DELETE = $4;
    SHCNE_MKDIR = $8;
    SHCNE_RMDIR = $10;
    SHCNE_MEDIAINSERTED = $20;
    SHCNE_MEDIAREMOVED = $40;
    SHCNE_DRIVEREMOVED = $80;
    SHCNE_DRIVEADD = $100;
    SHCNE_NETSHARE = $200;
    SHCNE_NETUNSHARE = $400;
    SHCNE_ATTRIBUTES = $800;
    SHCNE_UPDATEDIR = $1000;
    SHCNE_UPDATEITEM = $2000;
    SHCNE_SERVERDISCONNECT = $4000;
    SHCNE_UPDATEIMAGE = $8000;
    SHCNE_DRIVEADDGUI = $10000;
    SHCNE_RENAMEFOLDER = $20000;
    SHCNE_FREESPACE = $40000;
    SHCNE_ASSOCCHANGED = $8000000;
    SHCNE_DISKEVENTS = $2381F;
    SHCNE_GLOBALEVENTS = $C0581E0;
    SHCNE_ALLEVENTS = $7FFFFFFF;
    SHCNE_INTERRUPT = $80000000;
    SHCNF_IDLIST = 0;
    // LPITEMIDLIST
    SHCNF_PATHA = $1;
    // path name
    SHCNF_PRINTERA = $2;
    // printer friendly name
    SHCNF_DWORD = $3;
    // DWORD
    SHCNF_PATHW = $5;
    // path name
    SHCNF_PRINTERW = $6;
    // printer friendly name
    SHCNF_TYPE = $FF;
    SHCNF_FLUSH = $1000;
    SHCNF_FLUSHNOWAIT = $2000;
    SHCNF_PATH = SHCNF_PATHW;
    SHCNF_PRINTER = SHCNF_PRINTERW;
    WM_SHNOTIFY = $401;
    NOERROR = 0;
    
    type
    TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button1Click(Sender: TObject);
    private
    { Private declarations }
    public
    procedure WMShellReg(var Message:TMessage);message WM_SHNOTIFY; { Public declarations }
    end;
    
    type PSHNOTIFYSTRUCT=^SHNOTIFYSTRUCT;
    SHNOTIFYSTRUCT = record
    dwItem1 : PItemIDList;
    dwItem2 : PItemIDList;
    end;
    Type PSHFileInfoByte=^SHFileInfoByte;
    _SHFileInfoByte = record
    hIcon :Integer;
    iIcon :Integer;
    dwAttributes : Integer;
    szDisplayName : array [0..259] of char;
    szTypeName : array [0..79] of char;
    end;
    SHFileInfoByte=_SHFileInfoByte;
    Type PIDLSTRUCT = ^IDLSTRUCT;
    _IDLSTRUCT = record
    pidl : PItemIDList;
    bWatchSubFolders : Integer;
    end;
    IDLSTRUCT =_IDLSTRUCT;
    
    function SHNotify_Register(hWnd : Integer) : Bool;
    function SHNotify_UnRegister:Bool;
    function SHEventName(strPath1,strPath2:string;lParam:Integer):string;
    Function SHChangeNotifyDeregister(hNotify:integer):integer;stdcall;external 'Shell32.dll' index 4;
    Function SHChangeNotifyRegister(hWnd,uFlags,dwEventID,uMSG,cItems:LongWord;lpps:PIDLSTRUCT):integer;stdcall;external 'Shell32.dll' index 2;
    Function SHGetFileInfoPidl(pidl : PItemIDList;dwFileAttributes : Integer;psfib : PSHFILEINFOBYTE;cbFileInfo : Integer;uFlags : Integer):Integer;stdcall;external 'Shell32.dll' name 'SHGetFileInfoA';
    var
    Form1: TForm1;
    m_hSHNotify:Integer;
    m_pidlDesktop : PItemIDList;
    
    implementation
    
    {$R *.dfm}
    
    { TForm1 }
    
    function SHEventName(strPath1,strPath2:string;lParam:Integer):string;
    var
    sEvent:String;
    begin
    case lParam of //根据参数设置提示消息
    SHCNE_RENAMEITEM: sEvent := '重命名文件'+strPath1+''+strpath2;
    SHCNE_CREATE: sEvent := '建立文件 文件名:'+strPath1;
    SHCNE_DELETE: sEvent := '删除文件 文件名:'+strPath1;
    SHCNE_MKDIR: sEvent := '新建目录 目录名:'+strPath1;
    SHCNE_RMDIR: sEvent := '删除目录 目录名:'+strPath1;
    SHCNE_MEDIAINSERTED: sEvent := strPath1+'中插入可移动存储介质';
    SHCNE_MEDIAREMOVED: sEvent := strPath1+'中移去可移动存储介质'+strPath1+' '+strpath2;
    SHCNE_DRIVEREMOVED: sEvent := '移去驱动器'+strPath1;
    SHCNE_DRIVEADD: sEvent := '添加驱动器'+strPath1;
    SHCNE_NETSHARE: sEvent := '改变目录'+strPath1+'的共享属性';
    
    SHCNE_ATTRIBUTES: sEvent := '改变文件目录属性 文件名'+strPath1;
    SHCNE_UPDATEDIR: sEvent := '更新目录'+strPath1;
    SHCNE_UPDATEITEM: sEvent := '更新文件 文件名:'+strPath1;
    SHCNE_SERVERDISCONNECT: sEvent := '断开与服务器的连接'+strPath1+' '+strpath2;
    SHCNE_UPDATEIMAGE: sEvent := 'SHCNE_UPDATEIMAGE';
    SHCNE_DRIVEADDGUI: sEvent := 'SHCNE_DRIVEADDGUI';
    SHCNE_RENAMEFOLDER: sEvent := '重命名文件夹'+strPath1+''+strpath2;
    SHCNE_FREESPACE: sEvent := '磁盘空间大小改变';
    SHCNE_ASSOCCHANGED: sEvent := '改变文件关联';
    else
    sEvent:='未知操作'+IntToStr(lParam);
    end;
    Result:=sEvent;
    end;
    
    function SHNotify_Register(hWnd : Integer) : Bool;
    var
    ps: pidlstruct;
    begin
    {$R-}
    result := false;
    if m_hshnotify = 0 then begin
    //获取桌面文件夹的pidl
    if shgetspecialfolderlocation(0, CSIDL_DESKTOP, m_pidldesktop) <> noerror then
    form1.close;
    if boolean(m_pidldesktop) then begin
    new(ps);
    try
    ps.bwatchsubfolders := 1;
    ps.pidl := m_pidldesktop;
    
    // 利用shchangenotifyregister函数注册系统消息处理
    m_hshnotify := shchangenotifyregister(hwnd, (shcnf_type or shcnf_idlist),
    (shcne_allevents or shcne_interrupt),
    wm_shnotify, 1, ps);
    result := boolean(m_hshnotify);
    finally
    FreeMem(ps);
    end;
    end
    else
    // 如果出现错误就使用 cotaskmemfree函数来释放句柄
    cotaskmemfree(m_pidldesktop);
    end;
    {$R+}
    end;
    
    function SHNotify_UnRegister:Bool;
    begin
    Result:=False;
    If Boolean(m_hSHNotify) Then
    //取消系统消息监视,同时释放桌面的Pidl
    If Boolean(SHChangeNotifyDeregister(m_hSHNotify)) Then begin
    {$R-}
    m_hSHNotify := 0;
    CoTaskMemFree(m_pidlDesktop);
    Result := True;
    {$R-}
    End;
    end;
    
    procedure TForm1.WMShellReg(var Message: TMessage);
    //file://系统消息处理函数
    var
    strPath1,strPath2:String;
    charPath:array[0..259]of char;
    pidlItem:PSHNOTIFYSTRUCT;
    begin
    pidlItem:=PSHNOTIFYSTRUCT(Message.wParam);
    //file://获得系统消息相关得路径
    SHGetPathFromIDList(pidlItem.dwItem1,charPath);
    strPath1:=charPath;
    SHGetPathFromIDList(pidlItem.dwItem2,charPath);
    strPath2:=charPath;
    Memo1.Lines.Add(SHEvEntName(strPath1,strPath2,Message.lParam)+chr(13)+chr(10));
    end;
    
    procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
    //在程序退出的同时删除监视
    if Boolean(m_pidlDesktop) then
    SHNotify_Unregister;
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    begin
    m_hSHNotify:=0;
    if SHNotify_Register(Form1.Handle) then begin //file://注册Shell监视
    ShowMessage('Shell监视程序成功注册');
    Button1.Enabled := False;
    end
    else
    ShowMessage('Shell监视程序注册失败');
    end;
    
    end.
    View Code

    运行程序,点击“打开监视”按钮,如果出现一个显示“Shell监视程序成功注册”的对话框,说明Form1已经加入到系统操作监视链中了,你可以试着在资源管理器中建立、删除文件夹,移动文件等操作,你可以发现这些操作都被记录下来并显示在文本框中。

    在上面的程序中多次使用到了一个PItemIDList的结构,这个数据结构指定Windows下得一个“项目”,在Windows下资源实现统一管理一个“项目”可以是一个文件或者一个文件夹,也可以是一个打印机等资源。另外一些API函数也涉及到了Shell(Windows外壳)操作,各位读者可以参考相应的参考资料。

    由于使用到了Windows的未公开函数,没有相关得参考资料,所以有一些未知得操作(在Memo1中会显示“未知操作”)。如果哪位读者有兴趣, http://member.netease.com/~blackcat 有实现该功能的VB程序下载。

    以上程序在Windows98、Windows2000、Delphi5下运行通过。

    如果需要检测某个文件夹,可使用以下方法:

    function TDyjPlatDirMonitor.RegisterDirMonitor(hWnd: Integer;
    aPath: string): Boolean;
    var
    _vP : PWideChar;
    _vPs : IDLSTRUCT;
    begin
    {$R-}
    Result := False;
    if FSHNotify = 0 then
    begin
    _vP := PWideChar(WideString(aPath));
    FPathPidl := SHSimpleIDListFromPath(_vP);
    if Boolean(FPathPidl) then
    begin
    _vPs.bWatchSubFolders := 1;
    _vPs.pidl := FPathPidl;
    FSHNotify := SHChangeNotifyRegister(hWnd,
    (SHCNF_TYPE or SHCNF_IDLIST),
    (SHCNE_ALLEVENTS or SHCNE_INTERRUPT),
    WM_SHNOTIFY, 1, @_vPs);
    Result := Boolean(FSHNotify);
    end
    else
    CoTaskMemFree(FPathPidl);
    end;
    {$R+ }
    end;
    View Code

    监控系统文件操作

    这里介绍一种利用Windows未公开函数实现这个功能的方法。

    在Windows下有一个未公开函数SHChangeNotifyRegister可以把你的窗口添加到系统的系统消息监视链中,该函数在Delphi中的定义如下:

    Function SHChangeNotifyRegister(hWnd,uFlags,dwEventID,uMSG,cItems:LongWord;
    lpps:PIDLSTRUCT):integer;stdcall;external 'Shell32.dll' index 2;

    其中参数hWnd定义了监视系统操作的窗口得句柄,参数uFlags dwEventID定义监视操作参数,参数uMsg定义操作消息,参数cItems定义附加参数,参数lpps指定一个PIDLSTRUCT结构,该结构指定监视的目录。

    当函数调用成功之后,函数会返回一个监视操作句柄,同时系统就会将hWnd指定的窗口加入到操作监视链中,当有文件操作发生时,系统会向hWnd发送uMsg指定的消息,我们只要在程序中加入该消息的处理函数就可以实现对系统操作的监视了。

    如果要退出程序监视,就要调用另外一个未公开得函数SHChangeNotifyDeregister来取消程序监视。

    下面是使用Delphi编写的具体程序实现范例,首先建立一个新的工程文件,然后在Form1中加入一个Button控件和一个Memo控件,

    程序的代码如下:

    unit ufrmMain; 
    
    interface 
    
    uses 
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
    Dialogs ,shlobj, Activex, StdCtrls, 
    Menus, 
    uTbLogFile; 
    
    const 
    SHCNE_RENAMEITEM = $1; 
    SHCNE_CREATE = $2; 
    SHCNE_DELETE = $4; 
    SHCNE_MKDIR = $8; 
    SHCNE_RMDIR = $10; 
    SHCNE_MEDIAINSERTED = $20; 
    SHCNE_MEDIAREMOVED = $40; 
    SHCNE_DRIVEREMOVED = $80; 
    SHCNE_DRIVEADD = $100; 
    SHCNE_NETSHARE = $200; 
    SHCNE_NETUNSHARE = $400; 
    SHCNE_ATTRIBUTES = $800; 
    SHCNE_UPDATEDIR = $1000; 
    SHCNE_UPDATEITEM = $2000; 
    SHCNE_SERVERDISCONNECT = $4000; 
    SHCNE_UPDATEIMAGE = $8000; 
    SHCNE_DRIVEADDGUI = $10000; 
    SHCNE_RENAMEFOLDER = $20000; 
    SHCNE_FREESPACE = $40000; 
    SHCNE_ASSOCCHANGED = $8000000; 
    SHCNE_DISKEVENTS = $2381F; 
    SHCNE_GLOBALEVENTS = $C0581E0; 
    SHCNE_ALLEVENTS = $7FFFFFFF; 
    SHCNE_INTERRUPT = $80000000; 
    SHCNF_IDLIST = 0; 
    // LPITEMIDLIST 
    SHCNF_PATHA = $1; 
    // path name 
    SHCNF_PRINTERA = $2; 
    // printer friendly name 
    SHCNF_DWORD = $3; 
    // DWORD 
    SHCNF_PATHW = $5; 
    // path name 
    SHCNF_PRINTERW = $6; 
    // printer friendly name 
    SHCNF_TYPE = $FF; 
    SHCNF_FLUSH = $1000; 
    SHCNF_FLUSHNOWAIT = $2000; 
    SHCNF_PATH = SHCNF_PATHW; 
    SHCNF_PRINTER = SHCNF_PRINTERW; 
    WM_SHNOTIFY = $401; 
    NOERROR = 0; 
    
    type 
    TForm1 = class(TForm) 
    mmo1: TMemo; 
    Button1: TButton; 
    procedure FormCreate(Sender: TObject); 
    procedure FormClose(Sender: TObject; var Action: TCloseAction); 
    procedure btn1Click(Sender: TObject); 
    procedure FormDestroy(Sender: TObject); 
    private 
    WRITE_LOG : TRTLCriticalSection; 
    FLogWriterSetupForm: TTbLogFile; 
    public 
    procedure WMShellReg(var Message:TMessage);message WM_SHNOTIFY; 
    end; 
    
    type 
    PSHNOTIFYSTRUCT=^SHNOTIFYSTRUCT; 
    SHNOTIFYSTRUCT = record 
    dwItem1 : PItemIDList; 
    dwItem2 : PItemIDList; 
    end; 
    
    Type 
    PSHFileInfoByte=^SHFileInfoByte; 
    _SHFileInfoByte = record 
    hIcon :Integer; 
    iIcon :Integer; 
    dwAttributes : Integer; 
    szDisplayName : array [0..259] of char; 
    szTypeName : array [0..79] of char; 
    end; 
    
    SHFileInfoByte=_SHFileInfoByte; 
    
    Type PIDLSTRUCT = ^IDLSTRUCT; 
    _IDLSTRUCT = record 
    pidl : PItemIDList; 
    bWatchSubFolders : Integer; 
    end; 
    
    IDLSTRUCT = _IDLSTRUCT; 
    
    function SHNotify_Register(hWnd : Integer) : Bool; 
    function SHNotify_UnRegister:Bool; 
    function SHEventName(strPath1,strPath2:string;lParam:Integer):string; 
    Function SHChangeNotifyDeregister(hNotify:integer):integer;stdcall;external 'Shell32.dll' index 4; 
    Function SHChangeNotifyRegister(hWnd,uFlags,dwEventID,uMSG,cItems:LongWord;lpps:PIDLSTRUCT):integer;stdcall;external 'Shell32.dll' index 2; 
    Function SHGetFileInfoPidl(pidl : PItemIDList;dwFileAttributes : Integer;psfib : PSHFILEINFOBYTE;cbFileInfo : Integer;uFlags : Integer):Integer;stdcall;external 'Shell32.dll' name 'SHGetFileInfoA'; 
    
    var 
    Form1: TForm1; 
    m_hSHNotify:Integer; 
    m_pidlDesktop : PItemIDList; 
    
    implementation 
    
    {$R *.dfm} 
    
    function SHEventName(strPath1, strPath2: string; lParam: Integer): string; 
    var 
    sEvent:String; 
    begin 
    case lParam of //根据参数设置提示消息 
    SHCNE_RENAMEITEM: sEvent := '重命名文件' + strPath1 + '' + strpath2; 
    SHCNE_CREATE: sEvent := '建立文件 文件名:' + strPath1; 
    SHCNE_DELETE: sEvent := '删除文件 文件名:' + strPath1; 
    SHCNE_MKDIR: sEvent := '新建目录 目录名:' + strPath1; 
    SHCNE_RMDIR: sEvent := '删除目录 目录名:' + strPath1; 
    SHCNE_MEDIAINSERTED: sEvent := strPath1 + '中插入可移动存储介质'; 
    SHCNE_MEDIAREMOVED: sEvent := strPath1 + '中移去可移动存储介质' + strPath1 + ' '+strpath2; 
    SHCNE_DRIVEREMOVED: sEvent := '移去驱动器' + strPath1; 
    SHCNE_DRIVEADD: sEvent := '添加驱动器' + strPath1; 
    SHCNE_NETSHARE: sEvent := '改变目录' + strPath1 + '的共享属性'; 
    
    SHCNE_ATTRIBUTES: sEvent := '改变文件目录属性 文件名' + strPath1; 
    SHCNE_UPDATEDIR: sEvent := '更新目录' + strPath1; 
    SHCNE_UPDATEITEM: sEvent := '更新文件 文件名:' + strPath1; 
    SHCNE_SERVERDISCONNECT: sEvent := '断开与服务器的连接' + strPath1 + ' ' + strpath2; 
    SHCNE_UPDATEIMAGE: sEvent := 'SHCNE_UPDATEIMAGE'; 
    SHCNE_DRIVEADDGUI: sEvent := 'SHCNE_DRIVEADDGUI'; 
    SHCNE_RENAMEFOLDER: sEvent := '重命名文件夹' + strPath1 + '' + strpath2; 
    SHCNE_FREESPACE: sEvent := '磁盘空间大小改变'; 
    SHCNE_ASSOCCHANGED: sEvent := '改变文件关联'; 
    else 
    sEvent := '未知操作' + IntToStr(lParam); 
    end; 
    Result := sEvent; 
    end; 
    
    function SHNotify_Register(hWnd: Integer): Bool; 
    var 
    ps: pidlstruct; 
    begin 
    {$R-} 
    result := false; 
    if m_hshnotify = 0 then 
    begin 
    //获取桌面文件夹的pidl 
    if shgetspecialfolderlocation(0, CSIDL_DESKTOP, m_pidldesktop) <> noerror then 
    form1.close; 
    if boolean(m_pidldesktop) then begin 
    new(ps); 
    try 
    ps.bwatchsubfolders := 1; 
    ps.pidl := m_pidldesktop; 
    
    // 利用shchangenotifyregister函数注册系统消息处理 
    m_hshnotify := shchangenotifyregister(hwnd, (shcnf_type or shcnf_idlist), 
    (shcne_allevents or shcne_interrupt), 
    wm_shnotify, 1, ps); 
    result := boolean(m_hshnotify); 
    finally 
    FreeMem(ps); 
    end; 
    end 
    else 
    begin 
    // 如果出现错误就使用 cotaskmemfree函数来释放句柄 
    cotaskmemfree(m_pidldesktop); 
    end; 
    end; 
    {$R+} 
    end; 
    
    function SHNotify_UnRegister: Bool; 
    begin 
    Result := False; 
    If Boolean(m_hSHNotify) Then 
    begin 
    //取消系统消息监视,同时释放桌面的Pidl 
    If Boolean(SHChangeNotifyDeregister(m_hSHNotify)) Then begin 
    {$R-} 
    m_hSHNotify := 0; 
    CoTaskMemFree(m_pidlDesktop); 
    Result := True; 
    {$R-} 
    End; 
    end; 
    end; 
    
    procedure TForm1.WMShellReg(var Message: TMessage); 
    //file://系统消息处理函数 
    var 
    strPath1,strPath2:String; 
    charPath:array[0..259]of char; 
    pidlItem:PSHNOTIFYSTRUCT; 
    begin 
    pidlItem := PSHNOTIFYSTRUCT(Message.wParam); 
    //file://获得系统消息相关得路径 
    SHGetPathFromIDList(pidlItem.dwItem1, charPath); 
    strPath1 := charPath; 
    SHGetPathFromIDList(pidlItem.dwItem2, charPath); 
    strPath2 := charPath; 
    
    try 
    EnterCriticalSection(WRITE_LOG); 
    FLogWriterSetupForm.WriteLnLog(SHEvEntName(strPath1, strPath2, Message.lParam) + chr(13) + chr(10)); 
    finally 
    LeaveCriticalSection(WRITE_LOG); 
    end; 
    // mmo1.Lines.Add(SHEvEntName(strPath1,strPath2,Message.lParam)+chr(13)+chr(10)); 
    end; 
    
    {获得计算机名} 
    function GetComputerName: string; 
    var 
    buffer: array[0..MAX_COMPUTERNAME_LENGTH + 1] of Char; 
    Size: Cardinal; 
    begin 
    Size := MAX_COMPUTERNAME_LENGTH + 1; 
    Windows.GetComputerName(@buffer, Size); 
    Result := strpas(buffer); 
    end; 
    
    procedure TForm1.FormCreate(Sender: TObject); 
    begin 
    Caption := GetComputerName; 
    
    InitializeCriticalSection(WRITE_LOG); 
    FLogWriterSetupForm := TTbLogFile.Create(nil); 
    FLogWriterSetupForm.AutoRenameByDay := True; 
    FLogWriterSetupForm.Open(ExtractFilePath(ParamStr(0)) + ' 操作.log', otAppend); 
    end; 
    
    procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); 
    begin 
    //在程序退出的同时删除监视 
    if Boolean(m_pidlDesktop) then 
    SHNotify_Unregister; 
    end; 
    
    procedure TForm1.btn1Click(Sender: TObject); 
    begin 
    m_hSHNotify:=0; 
    if SHNotify_Register(Form1.Handle) then begin //file://注册Shell监视 
    ShowMessage('Shell监视程序成功注册'); 
    Button1.Enabled := False; 
    end 
    else 
    ShowMessage('Shell监视程序注册失败'); 
    end; 
    
    procedure TForm1.FormDestroy(Sender: TObject); 
    begin 
    DeleteCriticalSection(WRITE_LOG); 
    FreeAndNil(FLogWriterSetupForm); 
    end; 
    
    end. 
    View Code

    运行程序,点击“打开监视”按钮,如果出现一个显示“Shell监视程序成功注册”的对话框,说明Form1已经加入到系统操作监视链中了,你可以试着在资源管理器中建立、删除文件夹,移动文件等操作,你可以发现这些操作都被记录下来并显示在文本框中。

    在上面的程序中多次使用到了一个PItemIDList的结构,这个数据结构指定Windows下得一个“项目”,在Windows下资源实现统一管理一个“项目”可以是一个文件或者一个文件夹,也可以是一个打印机等资源。另外一些API函数也涉及到了Shell(Windows外壳)操作,各位读者可以参考相应的参考资料。

    以上程序在Windows98、Windows2000、Delphi5下运行通过。

    delphi监控文件夹

    (******************************************
      文件和目录监控
      当磁盘上有文件或目录操作时,产生事件
      使用方法:
     
      开始监控: PathWatch(Self.Handle, 'C:FtpFolder');
      解除监控:PathWatch(-1);
     
      在窗体中加消息监听
      private
        { Private declarations }
        procedure MsgListern(var Msg:TMessage);message WM_SHNOTIFY;
     
      实现:
      procedure TForm1.MsgListern(var Msg:TMessage);
      begin
        PathWatch(Msg,procedure(a,s1,s2:String) begin
          Log('文件事件是:'  +a);
          Log('文件名称是:'  +s1);
          Log('另外的参数是:'+s2);
        end);
      end;
     
    ******************************************)
    unit PathWatch;
     
    interface
     
    uses
      Winapi.Messages, System.SysUtils, FMX.Types, FMX.Platform.Win, WinAPI.ShlObj,
      Winapi.ActiveX, WinApi.Windows, VCL.Dialogs
      ;
     
    const
      WM_SHNOTIFY = $401;
     
    type
      PIDLSTRUCT = ^IDLSTRUCT;
      _IDLSTRUCT = record
        pidl : PItemIDList;
        bWatchSubFolders : Integer;
      end;
      IDLSTRUCT =_IDLSTRUCT;
    type
      PSHNOTIFYSTRUCT=^SHNOTIFYSTRUCT;
      SHNOTIFYSTRUCT = record
        dwItem1 : PItemIDList;
        dwItem2 : PItemIDList;
      end;
     
    Function SHChangeNotifyDeregister(hNotify:integer):integer;stdcall;
    external 'Shell32.dll' index 4;
     
    Function SHChangeNotifyRegister(hWnd,uFlags,dwEventID,uMSG,cItems:LongWord;lpps:PIDLSTRUCT):integer;stdcall;
    external 'Shell32.dll' index 2;
     
    function PathWatch(hWND: Integer      ; Path:String=''):Boolean; overload;
    function PathWatch(hWND: TWindowHandle; Path:String=''):Boolean; overload;
    function PathWatch(var Msg: TMessage; callback: TProc<String,String,String>):Boolean; overload;
     
    var
      g_HSHNotify   : Integer;
      g_pidlDesktop : PItemIDList;
      g_WatchPath   : String;
     
    implementation
     
    function PathWatch(hWND: Integer; Path:String=''):Boolean;
    var
      ps:PIDLSTRUCT;
    begin
      result:=False;
      Path:=Path.Replace('/','');
      if(hWnd>=0) then begin  //  开始监控
        g_WatchPath:=Path.ToUpper;
     
        if g_HSHNotify = 0 then begin
          SHGetSpecialFolderLocation(0, CSIDL_DESKTOP, g_pidlDesktop);
          if Boolean(g_pidlDesktop) then begin
            getmem(ps,sizeof(IDLSTRUCT));
            ps.bWatchSubFolders := 1;
            ps.pidl := g_pidlDesktop;
            g_HSHNotify := SHChangeNotifyRegister(hWnd, (SHCNF_TYPE Or SHCNF_IDLIST),(SHCNE_ALLEVENTS Or SHCNE_INTERRUPT),WM_SHNOTIFY, 1, ps);
            Result := Boolean(g_HSHNotify);
          end else CoTaskMemFree(g_pidlDesktop);
        end;
      end else begin  //  解除监控
        if boolean(g_HSHNotify) then if Boolean(SHChangeNotifyDeregister(g_HSHNotify)) then begin
          g_HSHNotify := 1;
          CoTaskMemFree(g_pidlDesktop);
          result := True;
        end;
      end;
    end;
     
    function PathWatch(hWND: TWindowHandle; Path:String=''):Boolean;
    begin
      PathWatch(FmxHandleToHWND(hWND),Path);  //  FireMonkey的窗体不接受处理Windows消息
    end;
     
    function PathWatch(var Msg: TMessage; callback:TProc<String,String,String>):Boolean;
    var
      a, s1,s2  : String;
      buf       : array[0..MAX_PATH] of char;
      pidlItem  : PSHNOTIFYSTRUCT;
    begin
      pidlItem :=PSHNOTIFYSTRUCT(Msg.WParam);
      SHGetPathFromIDList(pidlItem.dwItem1, buf); s1 := buf;
      SHGetPathFromIDList(pidlItem.dwItem2, buf); s2 := buf;
      a:='';
      case Msg.LParam of
    //    SHCNE_RENAMEITEM      : a := '重命名'       ;
        SHCNE_CREATE          : a := '建立文件'     ;
    //    SHCNE_DELETE          : a := '删除文件'     ;
    //    SHCNE_MKDIR           : a := '新建目录'     ;
    //    SHCNE_RMDIR           : a := '删除目录'     ;
    //    SHCNE_ATTRIBUTES      : a := '改变属性'     ;
    //    SHCNE_MEDIAINSERTED   : a := '插入介质'     ;
    //    SHCNE_MEDIAREMOVED    : a := '移去介质'     ;
    //    SHCNE_DRIVEREMOVED    : a := '移去驱动器'   ;
    //    SHCNE_DRIVEADD        : a := '添加驱动器'   ;
    //    SHCNE_NETSHARE        : a := '改变共享'     ;
    //    SHCNE_UPDATEDIR       : a := '更新目录'     ;
    //    SHCNE_UPDATEITEM      : a := '更新文件'     ;
    //    SHCNE_SERVERDISCONNECT: a := '断开连接'     ;
    //    SHCNE_UPDATEIMAGE     : a := '更新图标'     ;
    //    SHCNE_DRIVEADDGUI     : a := '添加驱动器'   ;
    //    SHCNE_RENAMEFOLDER    : a := '重命名文件夹' ;
    //    SHCNE_FREESPACE       : a := '磁盘空间改变' ;
    //    SHCNE_ASSOCCHANGED    : a := '改变文件关联' ;
    //  else                      a := '其他操作'     ;
     
      end;
      result := True;
      if( (a<>'') and (Assigned(callback)) and (s1.ToUpper.StartsWith(g_WatchPath))) and (not s1.Contains('_plate')) then
      begin
        callback(a,s1,g_WatchPath);
      end;
    end;
     
     
    end.
     调用:
    
    PathWatch(self.Handle, DM.Config.O['Local'].S['PhotoPath']);
    
    窗体中需要消息事件触发:
    
    procedure MsgListern(var Msg: TMessage); message WM_SHNOTIFY;     // 触发监听事件
    
    procedure TFormMain.MsgListern(var Msg: TMessage);
    begin
      PathWatch(Msg, Procedure(act,fn,s2: string) begin
        if(act='建立文件') then begin
          if SecondsBetween(now(), PrePostTime) >= 5 then    //两个时间之间相差的秒数
          begin
           // 这里处理监控到后   要响应的事情
          end;
        end;
      end);
    end;
    View Code

    监控指定文件夹

    delphi XE + XP 下测试通过
     
    O2DirSpy.pas    (该单元获取自网络)
    [delphi]  
    {====================================================================}  
    {   TOxygenDirectorySpy Component, v1.6 c 2000-2001 Oxygen Software  }  
    {--------------------------------------------------------------------}  
    {          Written by Oleg Fyodorov, delphi@oxygensoftware.com       }  
    {                  http://www.oxygensoftware.com                     }  
    {====================================================================}  
      
    unit O2DirSpy;  
      
    interface  
      
      uses Classes, Controls, Windows, SysUtils, ShellApi, Dialogs, Messages, FileCtrl;  
      
      type  
        TDirectoryChangeType = (ctNone, ctAttributes, ctSize, ctCreationTime, ctLastModificationTime, ctLastAccessTime, ctLastTime, ctCreate, ctRemove);  
      
        TOxygenDirectorySpy = class;  
      
        TDirectoryChangeRecord = record  
          Directory : String;  
          FileFlag : Boolean; // When True, ChangeType applies to a file; False - ChangeType applies to Directory  
          Name : String; // Name of changed file/directory  
          OldTime, NewTime : TDateTime;  // Significant only when ChangeType is one of ctCreationTime, ctLastModificationTime, ctLastAccessTime, ctLastTime  
          OldAttributes, NewAttributes : DWord; // Significant only when ChangeType is ctAttributes  
          OldSize, NewSize : DWord; // Significant only when ChangeType is ctSize  
          ChangeType : TDirectoryChangeType; // Describes a change type (creation, removing etc.)  
        end;  
      
        TSpySearchRec = record  
          Time: Integer;  
          Size: Integer;  
          Attr: Integer;  
          dwFileAttributes: DWORD;  
          ftCreationTime: TFileTime;  
          ftLastAccessTime: TFileTime;  
          ftLastWriteTime: TFileTime;  
          nFileSizeHigh: DWORD;  
          nFileSizeLow: DWORD;  
        end;  
      
        TFileData = class  
          private  
            FSearchRec : TSpySearchRec;  
            Name: TFileName;  
            FFound : Boolean;  
          public  
            constructor Create;  
            procedure Free;  
        end;  
      
        TFileDataList = class(TStringList)  
          private  
            function NewFileData(const FileName : String; sr : TSearchRec) : TFileData;  
            function GetFoundCount : Integer;  
          public  
            property FoundCount : Integer read GetFoundCount;  
      
            destructor Destroy; override;  
            function AddFileData(FileData : TFileData) : Integer;  
            function AddSearchRec(const Directory : String; sr : TSearchRec) : Integer;  
            procedure Delete(Index : Integer); override;  
            procedure Clear; override;  
            procedure SetFound(Value : Boolean);  
        end;  
      
        TReadDirChangesThread = class(TThread)  
        private  
          FOwner           : TOxygenDirectorySpy;  
          FDirectories     : TStringList;  
          FHandles         : TList;  
          FChangeRecord    : TDirectoryChangeRecord;  
          FFilesData,  
          FTempFilesData   : TFileDataList;  
          pHandles         : PWOHandleArray;  
          procedure ReleaseHandle;  
          procedure AllocateHandle;  
          procedure ReadDirectories(DestData : TFileDataList);  
          procedure CompareSearchRec(var srOld, srNew : TSpySearchRec);  
        protected  
          procedure Execute; override;  
          procedure Notify;  
        public  
          constructor Create(Owner : TOxygenDirectorySpy);  
          destructor Destroy; override;  
          procedure Reset;  
        end;  
      
        TChangeDirectoryEvent = procedure (Sender : TObject; ChangeRecord : TDirectoryChangeRecord) of object;  
      
        TOxygenDirectorySpy = class(TComponent)  
          private  
            FThread : TReadDirChangesThread;  
            FEnabled,  
            FWatchSubTree : Boolean;  
            FDirectories : TStrings;  
            FOnChangeDirectory : TChangeDirectoryEvent;  
      
            procedure SetEnabled(const Value : Boolean);  
            procedure CheckDirectories;  
            procedure SetDirectories(const Value : TStrings);  
            procedure SetWatchSubTree(const Value : Boolean);  
          protected  
            procedure DoChangeDirectory(ChangeRecord : TDirectoryChangeRecord);  
          published  
            property Enabled : Boolean read FEnabled write SetEnabled;  
            property Directories : TStrings read FDirectories write SetDirectories;  
            property WatchSubTree : Boolean read FWatchSubTree write SetWatchSubTree;  
            property OnChangeDirectory : TChangeDirectoryEvent read FOnChangeDirectory write FOnChangeDirectory;  
          public  
            constructor Create(AOwner : TComponent); override;  
            destructor Destroy; override;  
        end;  
      
        function ChangeRecord2String(ChangeRecord : TDirectoryChangeRecord) : String;  
      
        procedure Register;  
      
    implementation  
      
    function ChangeRecord2String(ChangeRecord : TDirectoryChangeRecord) : String;  
      var s : String;  
    begin  
      Result := 'No changes';  
      if ChangeRecord.FileFlag then s := 'File ' else s := 'Directory ';  
      s := s + '"' + ChangeRecord.Name + '"';  
      case ChangeRecord.ChangeType of  
        ctAttributes           : Result := s + ' attributes are changed. Old: ' + IntToHex(ChangeRecord.OldAttributes,8) + ', New: ' + IntToHex(ChangeRecord.NewAttributes,8);  
        ctSize                 : Result := s + ' size is changed. Old: ' + IntToStr(ChangeRecord.OldSize) + ', New: ' + IntToStr(ChangeRecord.NewSize);  
        ctCreationTime         : Result := s + ' creation time is changed. Old: ' + DateTimeToStr(ChangeRecord.OldTime) + ', New: ' + DateTimeToStr(ChangeRecord.NewTime);  
        ctLastModificationTime : Result := s + ' last modification time is changed. Old: ' + DateTimeToStr(ChangeRecord.OldTime) + ', New: ' + DateTimeToStr(ChangeRecord.NewTime);  
        ctLastAccessTime       : Result := s + ' last access time is changed. Old: ' + DateTimeToStr(ChangeRecord.OldTime) + ', New: ' + DateTimeToStr(ChangeRecord.NewTime);  
        ctLastTime             : Result := s + ' time is changed. Old: ' + DateTimeToStr(ChangeRecord.OldTime) + ', New: ' + DateTimeToStr(ChangeRecord.NewTime);  
        ctCreate               : Result := s + ' is created';  
        ctRemove               : Result := s + ' is deleted';  
      end;  
    end;  
      
    function  SameSystemTime(Time1, Time2 : TSystemTime) : Boolean;  
    begin  
      Result := ((Time1.wYear = Time2.wYear) and (Time1.wMonth = Time2.wMonth) and (Time1.wDay = Time2.wDay) and (Time1.wHour = Time2.wHour) and (Time1.wMinute = Time2.wMinute) and (Time1.wSecond = Time2.wSecond) and (Time1.wMilliseconds = Time2.wMilliseconds));  
    end;  
      
    function ReplaceText(s, SourceText, DestText: String):String;  
      var st,res:string;  
          i:Integer;  
    begin  
      ReplaceText:='';  
      if ((s='') or (SourceText='')) then Exit;  
      st:=s;  
      res:='';  
      i:=Pos(SourceText,s);  
      while (i>0) do  
      begin  
        res:=res+Copy(st,1,i-1)+DestText;  
        Delete(st,1,(i+Length(SourceText)-1));  
        i:=Pos(SourceText,st);  
      end;  
      res:=res+st;  
      ReplaceText:=res;  
    end;  
      
      
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////  
    // TFileData  
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////  
    constructor TFileData.Create;  
    begin  
      inherited Create;  
      Name := '';  
      FillChar(FSearchRec,SizeOf(FSearchRec),0);  
      FFound := False;  
    end;  
      
    procedure TFileData.Free;  
    begin  
      Name := '';  
      //Finalize(FSearchRec);  
      inherited Free;  
    end;  
      
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////  
    //  TFileDataList  
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////  
    destructor TFileDataList.Destroy;  
    begin  
      Clear;  
      inherited Destroy;;  
    end;  
      
    function TFileDataList.NewFileData(const FileName : String; sr : TSearchRec) : TFileData;  
    begin  
      Result := TFileData.Create;  
      Result.Name := FileName;  
      with Result.FSearchRec do begin  
        Time := sr.Time;  
        Size := sr.Size;  
        Attr := sr.Attr;  
        dwFileAttributes := sr.FindData.dwFileAttributes;  
        ftCreationTime := sr.FindData.ftCreationTime;  
        ftLastAccessTime := sr.FindData.ftLastAccessTime;  
        ftLastWriteTime := sr.FindData.ftLastWriteTime;  
        nFileSizeHigh := sr.FindData.nFileSizeHigh;  
        nFileSizeLow := sr.FindData.nFileSizeLow;  
      end;  
    end;  
      
    function TFileDataList.GetFoundCount : Integer;  
      var i : Integer;  
    begin  
      Result := 0;  
      for i := 1 to Count do if TFileData(Objects[i-1]).FFound then Inc(Result);  
    end;  
      
    function TFileDataList.AddFileData(FileData : TFileData) : Integer;  
      var fd : TFileData;  
    begin  
      fd := TFileData.Create;  
      fd.Name := FileData.Name;  
      fd.FSearchRec := FileData.FSearchRec;  
      Result := AddObject(fd.Name, fd);  
    end;  
      
    function TFileDataList.AddSearchRec(const Directory : String; sr : TSearchRec) : Integer;  
      var FileName : String;  
    begin  
      if (Directory <> '') then FileName := ReplaceText(Directory + '' + sr.Name,'\','') else FileName := sr.Name;  
      Result := AddObject(FileName, NewFileData(FileName, sr));  
    end;  
      
    procedure TFileDataList.Delete(Index : Integer);  
    begin  
      TFileData(Objects[Index]).Free;  
      inherited Delete(Index);  
    end;  
      
    procedure TFileDataList.Clear;  
    begin  
      while (Count > 0) do Delete(0);  
      inherited Clear;  
    end;  
      
    procedure TFileDataList.SetFound(Value : Boolean);  
      var i : Integer;  
    begin  
      for i := 1 to Count do TFileData(Objects[i-1]).FFound := Value;  
    end;  
      
    function CompareMem(fpBlock1, fpBlock2: Pointer; Size: Cardinal): Boolean; assembler;  
    asm  
            PUSH    ESI  
            PUSH    EDI  
            MOV     ESI,fpBlock1  
            MOV     EDI,fpBlock2  
            MOV     ECX,Size  
            MOV     EDX,ECX  
            XOR     EAX,EAX  
            AND     EDX,3  
            SHR     ECX,2  
            REPE    CMPSD  
            JNE     @@2  
            MOV     ECX,EDX  
            REPE    CMPSB  
            JNE     @@2  
    @@1:    INC     EAX  
    @@2:    POP     EDI  
            POP     ESI  
    end;  
      
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////  
    //       TReadDirChangesThread  
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////  
    procedure TReadDirChangesThread.CompareSearchRec(var srOld, srNew : TSpySearchRec);  
      var tt,nt,ot : TSystemTime;  
          //sro,srn : TSpySearchRec;  
    begin  
      FChangeRecord.ChangeType := ctNone;  
      if CompareMem(@srOld,@srNew, SizeOf(TSpySearchRec)) then Exit;  
      if (srOld.Time <> srNew.Time) then begin  
        FChangeRecord.ChangeType := ctLastTime;  
        FChangeRecord.OldTime := FileDateToDateTime(srOld.Time);  
        FChangeRecord.NewTime := FileDateToDateTime(srNew.Time);  
        srOld.Time := srNew.Time;  
        Exit;  
      end  
      else if (srOld.Size <> srNew.Size) then begin  
        FChangeRecord.ChangeType := ctSize;  
        FChangeRecord.OldSize := srOld.Size;  
        FChangeRecord.NewSize := srNew.Size;  
        srOld.Size := srNew.Size;  
        Exit;  
      end  
      else if (srOld.Attr <> srNew.Attr) or (srOld.dwFileAttributes <> srNew.dwFileAttributes) then begin  
        FChangeRecord.ChangeType := ctAttributes;  
        FChangeRecord.OldAttributes := srOld.dwFileAttributes;  
        FChangeRecord.NewAttributes := srNew.dwFileAttributes;  
        srOld.dwFileAttributes := srNew.dwFileAttributes;  
        srOld.Attr := srNew.Attr;  
        Exit;  
      end  
      else begin  
        FileTimeToSystemTime(srNew.ftCreationTime,nt);  
        SystemTimeToTzSpecificLocalTime(nil,nt,tt);  
        nt := tt;  
        FileTimeToSystemTime(srOld.ftCreationTime,ot);  
        SystemTimeToTzSpecificLocalTime(nil,ot,tt);  
        ot := tt;  
        if not SameSystemTime(nt,ot) then begin  
          FChangeRecord.ChangeType := ctCreationTime;  
          FChangeRecord.OldTime := SystemTimeToDateTime(ot);  
          FChangeRecord.NewTime := SystemTimeToDateTime(nt);  
          srOld.ftCreationTime := srNew.ftCreationTime;  
          Exit;  
        end  
        else begin  
          FileTimeToSystemTime(srNew.ftLastAccessTime,nt);  
          SystemTimeToTzSpecificLocalTime(nil,nt,tt);  
          nt := tt;  
          FileTimeToSystemTime(srOld.ftLastAccessTime,ot);  
          SystemTimeToTzSpecificLocalTime(nil,ot,tt);  
          ot := tt;  
          if not SameSystemTime(nt,ot) then begin  
            FChangeRecord.ChangeType := ctLastAccessTime;  
            FChangeRecord.OldTime := SystemTimeToDateTime(ot);  
            FChangeRecord.NewTime := SystemTimeToDateTime(nt);  
            srOld.ftLastAccessTime := srNew.ftLastAccessTime;  
            Exit;  
          end  
          else begin  
            FileTimeToSystemTime(srNew.ftLastWriteTime,nt);  
            SystemTimeToTzSpecificLocalTime(nil,nt,tt);  
            nt := tt;  
            FileTimeToSystemTime(srOld.ftLastWriteTime,ot);  
            SystemTimeToTzSpecificLocalTime(nil,ot,tt);  
            ot := tt;  
            if not SameSystemTime(nt,ot) then begin  
              FChangeRecord.ChangeType := ctLastModificationTime;  
              FChangeRecord.OldTime := SystemTimeToDateTime(ot);  
              FChangeRecord.NewTime := SystemTimeToDateTime(nt);  
              srOld.ftLastWriteTime := srNew.ftLastWriteTime;  
              Exit;  
            end;  
          end;  
        end;  
      end;  
    end;  
      
    procedure TReadDirChangesThread.Execute;  
      var i, Index : Integer;  
          R : DWord;  
          fd : TFileData;  
    begin  
      while not Terminated do try  
        if (FDirectories.Count = 0) or (not FOwner.Enabled) then Sleep(0)  
        else begin  
          R := WaitForMultipleObjects(FHandles.Count,pHandles,False,200);  
          if (R < (WAIT_OBJECT_0 + DWord(FHandles.Count))) then begin  
            FillChar(FChangeRecord,SizeOf(FChangeRecord),0);  
            FFilesData.SetFound(False);  
            FTempFilesData.Clear;  
            ReadDirectories(FTempFilesData);  
            while (FTempFilesData.Count > 0) do begin  
              fd := TFileData(FTempFilesData.Objects[0]);  
              // New file/directory is created  
              if not FFilesData.Find(fd.Name,Index) then begin  
                Index := FFilesData.AddFileData(fd);  
                TFileData(FFilesData.Objects[Index]).FFound := True;  
                FChangeRecord.ChangeType := ctCreate;  
                FChangeRecord.Name := fd.Name;  
                FChangeRecord.FileFlag := ((fd.FSearchRec.Attr and faDirectory) = 0);  
                FChangeRecord.Directory := FDirectories[R - WAIT_OBJECT_0];  
                Synchronize(Notify);  
              end  
              else begin  
                // file/directory is modified  
                TFileData(FFilesData.Objects[Index]).FFound := True;  
                CompareSearchRec(TFileData(FFilesData.Objects[Index]).FSearchRec, fd.FSearchRec);  
                while (FChangeRecord.ChangeType <> ctNone) do begin  
                  FChangeRecord.Name := fd.Name;  
                  FChangeRecord.FileFlag := ((fd.FSearchRec.Attr and faDirectory) = 0);  
                  FChangeRecord.Directory := FDirectories[R - WAIT_OBJECT_0];  
                  Synchronize(Notify);  
                  CompareSearchRec(TFileData(FFilesData.Objects[Index]).FSearchRec, fd.FSearchRec);  
                end;  
              end;  
              FTempFilesData.Delete(0);  
            end;  
            for i := FFilesData.Count downto 1 do if not TFileData(FFilesData.Objects[i-1]).FFound then begin  
              // file/directory is deleted  
              fd := TFileData(FFilesData.Objects[i-1]);  
              FChangeRecord.ChangeType := ctRemove;  
              FChangeRecord.Name := fd.Name;  
              FChangeRecord.FileFlag := ((fd.FSearchRec.Attr and faDirectory) = 0);  
              FChangeRecord.Directory := FDirectories[R - WAIT_OBJECT_0];  
              FFilesData.Delete(i-1);  
              Synchronize(Notify);  
            end;  
            FindNextChangeNotification(THandle(FHandles[R - WAIT_OBJECT_0]));  
          end;  
        end;  
      except end;  
    end;  
      
      
    procedure TReadDirChangesThread.Notify;  
      var cr : TDirectoryChangeRecord;  
    begin  
      cr := FChangeRecord;  
      if (cr.ChangeType <> ctNone) then FOwner.DoChangeDirectory(cr);  
    end;  
      
    constructor TReadDirChangesThread.Create(Owner : TOxygenDirectorySpy);  
    begin  
      inherited Create(True);  
      FOwner := Owner;  
      FHandles := TList.Create;  
      pHandles := nil;  
      FDirectories := TStringList.Create;  
      FDirectories.Sorted := True;  
      FDirectories.Duplicates := dupIgnore;  
      FreeOnTerminate := True;  
      FFilesData := TFileDataList.Create;  
      FFilesData.Sorted := True;  
      FFilesData.Duplicates := dupIgnore;  
      FTempFilesData := TFileDataList.Create;  
      FTempFilesData.Sorted := True;  
      FTempFilesData.Duplicates := dupIgnore;  
      //Reset;  
    end;  
      
    procedure TReadDirChangesThread.ReleaseHandle;  
      var i : Integer;  
    begin  
      if (pHandles <> nil) then FreeMem(pHandles,FHandles.Count * SizeOf(THandle));  
      pHandles := nil;  
      for i := 1 to FHandles.Count do if (THandle(FHandles[i-1]) <> INVALID_HANDLE_VALUE) then FindCloseChangeNotification(THandle(FHandles[i-1]));//CloseHandle(FHandle);  
      FHandles.Clear;  
    end;  
      
    destructor TReadDirChangesThread.Destroy;  
    begin  
      ReleaseHandle;  
      FHandles.Free;  
      FDirectories.Free;  
      FFilesData.Clear;  
      FFilesData.Free;  
      FTempFilesData.Clear;  
      FTempFilesData.Free;  
      inherited Destroy;  
    end;  
      
    procedure TReadDirChangesThread.AllocateHandle;  
      var i : Integer;  
          h : THandle;  
    begin  
      if (FOwner <> nil) then for i := 1 to FDirectories.Count do begin  
        h := FindFirstChangeNotification(PChar(FDirectories[i-1]), FOwner.WatchSubTree, FILE_NOTIFY_CHANGE_FILE_NAME +  
                                               FILE_NOTIFY_CHANGE_DIR_NAME + FILE_NOTIFY_CHANGE_ATTRIBUTES +  
                                               FILE_NOTIFY_CHANGE_SIZE + FILE_NOTIFY_CHANGE_LAST_WRITE);  
        {h := FindFirstChangeNotification(PChar(FDirectories[i-1]), FALSE, FILE_NOTIFY_CHANGE_FILE_NAME + 
                                               FILE_NOTIFY_CHANGE_DIR_NAME + FILE_NOTIFY_CHANGE_ATTRIBUTES + 
                                               FILE_NOTIFY_CHANGE_SIZE + FILE_NOTIFY_CHANGE_LAST_WRITE);}  
        if (h <> INVALID_HANDLE_VALUE) then FHandles.Add(Pointer(h)) else raise Exception.Create('Error allocating handle: #'+IntToStr(GetLastError));  
      end;  
      GetMem(pHandles,FHandles.Count * SizeOf(THandle));  
      for i := 1 to FHandles.Count do pHandles^[i-1] := THandle(FHandles[i-1]);  
      ReadDirectories(FFilesData);  
    end;  
      
    procedure TReadDirChangesThread.ReadDirectories(DestData : TFileDataList);  
      var i : Integer;  
      
      procedure AppendDirContents(const Directory : String);  
        var sr : TSearchRec;  
            s : String;  
      begin  
        if (Directory[Length(Directory)] <> '') then s := Directory + '*.*' else s := Directory + '*.*';  
        if (FindFirst(s,faAnyFile,sr) = 0) then begin  
          if (sr.Name <> '.') and (sr.Name <> '..') then begin  
            DestData.AddSearchRec(Directory,sr);  
            if ((sr.Attr and faDirectory) <> 0) and FOwner.WatchSubTree then AppendDirContents(Directory + '' + sr.Name);  
          end;  
          while (FindNext(sr) = 0) do if (sr.Name <> '.') and (sr.Name <> '..') then begin  
            DestData.AddSearchRec(Directory,sr);  
            if ((sr.Attr and faDirectory) <> 0) and FOwner.WatchSubTree then AppendDirContents(Directory + '' + sr.Name);  
          end;  
          FindClose(sr);  
        end;  
      end;  
      
    begin  
      for i := 1 to FDirectories.Count do AppendDirContents(FDirectories[i-1]);  
    end;  
      
    procedure TReadDirChangesThread.Reset;  
    begin  
      ReleaseHandle;  
      if (FDirectories.Count = 0) then Exit;  
      AllocateHandle;  
      if (FHandles.Count > 0) then Resume;  
    end;  
      
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////  
    //       TOxygenDirectorySpy  
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////  
    constructor TOxygenDirectorySpy.Create(AOwner : TComponent);  
    begin  
      inherited Create(AOwner);  
      FEnabled := False;  
      FWatchSubTree := False;  
      FDirectories := TStringList.Create;  
      TStringList(FDirectories).Sorted := True;  
      TStringList(FDirectories).Duplicates := dupIgnore;  
      FOnChangeDirectory := nil;  
      FThread := nil;  
    {$IFDEF O2_SW}  
      if (MessageDlg('This version of TOxygenDirectorySpy is NOT REGISTERED. '+#13#10+  
                     'Press Ok to visit http://www.oxygensoftware.com and register.',  
                     mtWarning,[mbOk,mbCancel],0) = mrOk) then ShellExecute(0,'open','http://www.oxygensoftware.com',nil,nil,SW_SHOWNORMAL);  
    {$ENDIF}  
    end;  
      
    procedure TOxygenDirectorySpy.SetEnabled(const Value : Boolean);  
    begin  
      if (csDesigning in ComponentState) then Exit;  
      if (Value = FEnabled) then Exit;  
      CheckDirectories;  
      if (FDirectories.Count = 0) then FEnabled := False else FEnabled := Value;  
      if (Win32Platform = VER_PLATFORM_WIN32_WINDOWS) then FWatchSubTree := False;  
      if FEnabled then begin  
        FThread := TReadDirChangesThread.Create(Self);  
        FThread.FDirectories.Clear;  
        FThread.FDirectories.AddStrings(FDirectories);  
        FThread.Reset;  
      end  
      else if (FThread <> nil) then begin  
        FThread.Terminate;  
        FThread.WaitFor;  
        //TerminateThread(FThread.Handle,0);  
        FThread := nil;  
      end;  
    end;  
      
    procedure TOxygenDirectorySpy.CheckDirectories;  
      var i : Integer;  
          s : String;  
    begin  
      for i := FDirectories.Count downto 1 do begin  
        s := Trim(FDirectories[i-1]);  
        if (s = '') or (not DirectoryExists(s)) then FDirectories.Delete(i-1);  
      end;  
      while (FDirectories.Count > MAXIMUM_WAIT_OBJECTS) do FDirectories.Delete(FDirectories.Count - 1);  
    end;  
      
    procedure TOxygenDirectorySpy.SetDirectories(const Value : TStrings);  
    begin  
      FDirectories.Clear;  
      FDirectories.AddStrings(Value);  
      CheckDirectories;  
      if FEnabled then begin  
        SetEnabled(False);  
        SetEnabled(True);  
      end;  
    end;  
      
    procedure TOxygenDirectorySpy.SetWatchSubTree(const Value : Boolean);  
    begin  
      if (Win32Platform = VER_PLATFORM_WIN32_WINDOWS) then begin  
        FWatchSubTree := False;  
        Exit;  
      end;  
      if (FWatchSubTree = Value) then Exit;  
      FWatchSubTree := Value;  
      if FEnabled then begin  
        SetEnabled(False);  
        SetEnabled(True);  
      end;  
    end;  
      
    procedure TOxygenDirectorySpy.DoChangeDirectory(ChangeRecord : TDirectoryChangeRecord);  
    begin  
      if Assigned(FOnChangeDirectory) then FOnChangeDirectory(Self, ChangeRecord);  
    end;  
      
    destructor TOxygenDirectorySpy.Destroy;  
    begin  
      if (FThread <> nil) then begin  
        FThread.Terminate;  
        FThread.WaitFor;  
        //TerminateThread(FThread.Handle,0);  
        //FThread.Free;  
        FThread := nil;  
      end;  
      inherited Destroy;  
    end;  
      
    procedure Register;  
    begin  
      RegisterComponents('Oxygen', [TOxygenDirectorySpy]);  
    end;  
      
      
    end.  
     
     
    调用单元
    [delphi]  
    unit utMain;  
      
    interface  
      
    uses  
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,  
      Dialogs, StdCtrls, ExtCtrls, O2DirSpy, FileCtrl;  
      
    type  
      TMainForm = class(TForm)  
        lstChanges: TListBox;  
        pnl1: TPanel;  
        pnl2: TPanel;  
        pnl3: TPanel;  
        btnAdd: TButton;  
        btnRemove: TButton;  
        pnl4: TPanel;  
        lstDirectoriesListBox: TListBox;  
        pnl5: TPanel;  
        lbl1: TLabel;  
        chkWatchSubTree: TCheckBox;  
        procedure btnAddClick(Sender: TObject);  
        procedure btnRemoveClick(Sender: TObject);  
        procedure FormCreate(Sender: TObject);  
        procedure chkWatchSubTreeClick(Sender: TObject);  
        procedure FormDestroy(Sender: TObject);  
      private  
        OxygenDirectorySpy1: TOxygenDirectorySpy;  
        procedure OxygenDirectorySpy1ChangeDirectory(Sender: TObject;  
          ChangeRecord: TDirectoryChangeRecord);  
        { Private declarations }  
      public  
        { Public declarations }  
      end;  
      
    var  
      MainForm: TMainForm;  
      
    implementation  
      
    {$R *.dfm}  
      
    procedure TMainForm.btnAddClick(Sender: TObject);  
      var s : String;  
    begin  
      if not SelectDirectory(s, [sdAllowCreate, sdPerformCreate, sdPrompt],0) then Exit;  
      with OxygenDirectorySpy1 do begin  
        Enabled := False;  
        Directories.Add(s);  
        Enabled := True;  
      end;  
      
      with lstDirectoriesListBox do try  
        Items.Clear;  
        Items.AddStrings(OxygenDirectorySpy1.Directories);  
        ItemIndex := 0;  
      except end;  
      btnRemove.Enabled := True;  
      
    end;  
      
    procedure TMainForm.btnRemoveClick(Sender: TObject);  
    var  
      i : Integer;  
    begin  
      if (lstDirectoriesListBox.Items.Count = 0) then Exit;  
      i := lstDirectoriesListBox.ItemIndex;  
      if (i = -1) then Exit;  
      lstDirectoriesListBox.Items.Delete(i);  
      with OxygenDirectorySpy1 do begin  
        Enabled := False;  
        Directories.Delete(i);  
        if (Directories.Count > 0) then begin  
          Enabled := True;  
          lstDirectoriesListBox.ItemIndex := 0;  
        end;  
      end;  
      btnRemove.Enabled := (lstDirectoriesListBox.Items.Count > 0);  
    end;  
      
    procedure TMainForm.chkWatchSubTreeClick(Sender: TObject);  
    begin  
      OxygenDirectorySpy1.WatchSubTree := chkWatchSubTree.Checked;  
    end;  
      
    procedure TMainForm.FormCreate(Sender: TObject);  
    begin  
      OxygenDirectorySpy1 := TOxygenDirectorySpy.Create(Self);  
      OxygenDirectorySpy1.OnChangeDirectory := OxygenDirectorySpy1ChangeDirectory;  
      SendMessage(lstChanges.Handle,LB_SETHORIZONTALEXTENT,1000,0);  
    end;  
      
    procedure TMainForm.FormDestroy(Sender: TObject);  
    begin  
      OxygenDirectorySpy1.Free;  
    end;  
      
    procedure TMainForm.OxygenDirectorySpy1ChangeDirectory(Sender: TObject; ChangeRecord: TDirectoryChangeRecord);  
    begin  
      lstChanges.Items.Add(DateTimeToStr(SysUtils.Now) + '  ' + ChangeRecord2String(ChangeRecord));  
      with lstChanges do if (Items.Count > 0) then ItemIndex := Items.Count - 1;  
    end;  
      
    end.  
     
     
    调用窗体
    [delphi]  
    object MainForm: TMainForm  
      Left = 0  
      Top = 0  
      Caption = 'MainForm'  
      ClientHeight = 388  
      ClientWidth = 485  
      Color = clBtnFace  
      Font.Charset = DEFAULT_CHARSET  
      Font.Color = clWindowText  
      Font.Height = -12  
      Font.Name = 'Tahoma'  
      Font.Style = []  
      OldCreateOrder = False  
      OnCreate = FormCreate  
      OnDestroy = FormDestroy  
      PixelsPerInch = 106  
      TextHeight = 14  
      object lstChanges: TListBox  
        Left = 0  
        Top = 105  
        Width = 485  
        Height = 283  
        Align = alClient  
        ItemHeight = 14  
        TabOrder = 0  
      end  
      object pnl1: TPanel  
        Left = 0  
        Top = 0  
        Width = 485  
        Height = 105  
        Align = alTop  
        TabOrder = 1  
        object pnl2: TPanel  
          Left = 405  
          Top = 1  
          Width = 79  
          Height = 103  
          Align = alRight  
          BevelOuter = bvNone  
          TabOrder = 0  
          object pnl3: TPanel  
            Left = 4  
            Top = 0  
            Width = 75  
            Height = 103  
            Align = alRight  
            BevelOuter = bvNone  
            TabOrder = 0  
            object btnAdd: TButton  
              Left = 4  
              Top = 24  
              Width = 69  
              Height = 21  
              Caption = 'Add'  
              TabOrder = 0  
              OnClick = btnAddClick  
            end  
            object btnRemove: TButton  
              Left = 4  
              Top = 52  
              Width = 69  
              Height = 21  
              Caption = 'Remove'  
              Enabled = False  
              TabOrder = 1  
              OnClick = btnRemoveClick  
            end  
          end  
        end  
        object pnl4: TPanel  
          Left = 1  
          Top = 1  
          Width = 404  
          Height = 103  
          Align = alClient  
          BevelOuter = bvNone  
          TabOrder = 1  
          object lstDirectoriesListBox: TListBox  
            Left = 0  
            Top = 29  
            Width = 404  
            Height = 74  
            Align = alClient  
            ItemHeight = 14  
            TabOrder = 0  
          end  
          object pnl5: TPanel  
            Left = 0  
            Top = 0  
            Width = 404  
            Height = 29  
            Align = alTop  
            BevelOuter = bvNone  
            TabOrder = 1  
            object lbl1: TLabel  
              Left = 5  
              Top = 8  
              Width = 115  
              Height = 14  
              Caption = 'Directories to watch:'  
            end  
            object chkWatchSubTree: TCheckBox  
              Left = 220  
              Top = 4  
              Width = 125  
              Height = 17  www.2cto.com
              Caption = 'Watch subdirectories'  
              Checked = True  
              State = cbChecked  
              TabOrder = 0  
              OnClick = chkWatchSubTreeClick  
            end  
          end  
        end  
      end  
    end  
    View Code

    监控文件夹

    const
      SHCNE_RENAMEITEM = $1;
      SHCNE_Create = $2;
      SHCNE_Delete = $4;
      SHCNE_MKDIR = $8;
      SHCNE_RMDIR = $10;
      SHCNE_MEDIAInsertED = $20;
      SHCNE_MEDIAREMOVED = $40;
      SHCNE_DRIVEREMOVED = $80;
      SHCNE_DRIVEADD = $100;
      SHCNE_NETSHARE = $200;
      SHCNE_NETUNSHARE = $400;
      SHCNE_ATTRIBUTES = $800;
      SHCNE_UpdateDIR = $1000;
      SHCNE_UpdateITEM = $2000;
      SHCNE_SERVERDISCONNECT = $4000;
      SHCNE_UpdateIMAGE = $8000;
      SHCNE_DRIVEADDGUI = $10000;
      SHCNE_RENAMEFOLDER = $20000;
      SHCNE_FREESPACE = $40000;
      SHCNE_ASSOCCHANGED = $8000000;
      SHCNE_DISKEVENTS = $2381F;
      SHCNE_GLOBALEVENTS = $C0581E0;
      SHCNE_ALLEVENTS = $7FFFFFFF;
      SHCNE_INTERRUPT = $80000000;
      SHCNF_IDLIST = 0;
      // LPITEMIDLIST
      SHCNF_PATHA = $1;
      // path name
      SHCNF_PRINTERA = $2;
      // printer friendly name
      SHCNF_DWORD = $3;
      // DWORD
      SHCNF_PATHW = $5;
      // path name
      SHCNF_PRINTERW = $6;
      // printer friendly name
      SHCNF_TYPE = $FF;
      SHCNF_FLUSH = $1000;
      SHCNF_FLUSHNOWAIT = $2000;
      SHCNF_PATH = SHCNF_PATHW;
      SHCNF_PRINTER = SHCNF_PRINTERW;
      WM_SHNOTIFY = $401;
      NOERROR = 0;
    
    type
      TForm1 = class(TForm)
        Button1: TButton;
        Memo1: TMemo;
        procedure Button1Click(Sender: TObject);
        procedure FormClose(Sender: TObject; var Action: TCloseAction);
    
      private
        procedure WMShellReg(var Message: TMessage); message WM_SHNOTIFY;
      public
        { Public declarations }
      end;
    
    type
      PSHNOTIFYSTRUCT = ^SHNOTIFYSTRUCT;
    
      SHNOTIFYSTRUCT = record
        dwItem1: PItemIDList;
        dwItem2: PItemIDList;
      end;
    
    type
      PSHFileInfoByte = ^SHFileInfoByte;
    
      _SHFileInfoByte = record
        hIcon: Integer;
        iIcon: Integer;
        dwAttributes: Integer;
        szDisplayName: array [0 .. 259] of char;
        szTypeName: array [0 .. 79] of char;
      end;
    
      SHFileInfoByte = _SHFileInfoByte;
    
    type
      PIDLSTRUCT = ^IDLSTRUCT;
    
      _IDLSTRUCT = record
        pidl: PItemIDList;
        bWatchSubFolders: Integer;
      end;
    
      IDLSTRUCT = _IDLSTRUCT;
    
    function SHNotify_Register(hWnd: Integer): Bool;
    function SHNotify_UnRegister: Bool;
    function SHEventName(strPath1, strPath2: string; lParam: Integer): string;
    function SHChangeNotifyDeregister(hNotify: Integer): Integer; stdcall;
    external 'Shell32.dll' index 4;
    function SHChangeNotifyRegister(hWnd, uFlags, dwEventID, uMSG,
      cItems: LongWord; lpps: PIDLSTRUCT): Integer; stdcall;
    external 'Shell32.dll' index 2;
    function SHGetFileInfoPidl(pidl: PItemIDList; dwFileAttributes: Integer;
      psfib: PSHFileInfoByte; cbFileInfo: Integer; uFlags: Integer): Integer;
      stdcall; external 'Shell32.dll' name 'SHGetFileInfoA';
    
    var
      Form1: TForm1;
      m_hSHNotify: Integer;
      m_pidlDesktop: PItemIDList;
    implementation
    
    { uses
      Graphics;
    }
    {$R *.dfm}
    
    function SHEventName(strPath1, strPath2: string; lParam: Integer): string;
    var
      sEvent: string;
    begin
      case lParam of // 根据参数设置提示消息
        SHCNE_RENAMEITEM:
          sEvent := '重命名文件' + strPath1 + '' + strPath2;
        SHCNE_Create:
          sEvent := '建立文件 文件名:' + strPath1;
        SHCNE_Delete:
          sEvent := '删除文件 文件名:' + strPath1;
        SHCNE_MKDIR:
          sEvent := '新建目录 目录名:' + strPath1;
        SHCNE_RMDIR:
          sEvent := '删除目录 目录名:' + strPath1;
        SHCNE_MEDIAInsertED:
          sEvent := strPath1 + '中插入可移动存储介质';
        SHCNE_MEDIAREMOVED:
          sEvent := strPath1 + '中移去可移动存储介质' + strPath1 + ' ' + strPath2;
        SHCNE_DRIVEREMOVED:
          sEvent := '移去驱动器' + strPath1;
        SHCNE_DRIVEADD:
          sEvent := '添加驱动器' + strPath1;
        SHCNE_NETSHARE:
          sEvent := '改变目录' + strPath1 + '的共享属性';
        SHCNE_ATTRIBUTES:
          sEvent := '改变文件目录属性 文件名' + strPath1;
        SHCNE_UpdateDIR:
          sEvent := '更新目录' + strPath1;
        SHCNE_UpdateITEM:
          sEvent := '更新文件 文件名:' + strPath1;
        SHCNE_SERVERDISCONNECT:
          sEvent := '断开与服务器的连接' + strPath1 + ' ' + strPath2;
        SHCNE_UpdateIMAGE:
          sEvent := 'SHCNE_UpdateIMAGE';
        SHCNE_DRIVEADDGUI:
          sEvent := 'SHCNE_DRIVEADDGUI';
        SHCNE_RENAMEFOLDER:
          sEvent := '重命名文件夹' + strPath1 + '' + strPath2;
        SHCNE_FREESPACE:
          sEvent := '磁盘空间大小改变';
        SHCNE_ASSOCCHANGED:
          sEvent := '改变文件关联';
      else
        sEvent := '未知操作' + IntToStr(lParam);
      end;
      Result := sEvent;
    end;
    
    function SHNotify_Register(hWnd: Integer): Bool;
    var
      ps: IDLSTRUCT;
    begin
    {$R-}
      Result := False;
      if m_hSHNotify = 0 then
      begin
        // 获取桌面文件夹的Pidl
        if SHGetSpecialFolderLocation(0, CSIDL_DESKTOP, m_pidlDesktop)
          <> NOERROR then
        begin
          Form1.close;
        end;
        if Boolean(m_pidlDesktop) then
        begin
          ps.bWatchSubFolders := 1;
          ps.pidl := m_pidlDesktop;
          // 利用SHChangeNotifyRegister函数注册系统消息处理
          m_hSHNotify := SHChangeNotifyRegister(hWnd, (SHCNF_TYPE or SHCNF_IDLIST),
            (SHCNE_ALLEVENTS or SHCNE_INTERRUPT), WM_SHNOTIFY, 1, @ps);
          Result := Boolean(m_hSHNotify); // mmmmmmmm
        end
        else
          // 如果出现错误就使用 CoTaskMemFree函数来释放句柄
          CoTaskMemFree(m_pidlDesktop);
      end;
    {$R+ }
    end;
    
    function SHNotify_UnRegister: Bool;
    begin
      Result := False;
      if Boolean(m_hSHNotify) then
      begin
        // 取消系统消息监视,同时释放桌面的Pidl
        if Boolean(SHChangeNotifyDeregister(m_hSHNotify)) then
        begin
    {$R-}
          m_hSHNotify := 0;
          CoTaskMemFree(m_pidlDesktop);
          Result := True;
    {$R-}
        end;
      end;
    end;
    
    
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      m_hSHNotify := 0;
      if SHNotify_Register(self.Handle) then
      begin // 注册Shell监视
        ShowMessage('Shell监视程序成功注册');
        Button1.Enabled := False;
      end
      else
        ShowMessage('Shell监视程序注册失败');
    end;
    
    procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
      if Boolean(m_pidlDesktop) then
        SHNotify_UnRegister;
    end;
    
    procedure TForm1.WMShellReg(var Message: TMessage);
    var
      strPath1, strPath2: string;
      charPath: array [0 .. 259] of char;
      pidlItem: PSHNOTIFYSTRUCT;
    begin
      pidlItem := PSHNOTIFYSTRUCT(Message.wParam);
      // 获得系统消息相关得路径
      SHGetPathFromIDList(pidlItem.dwItem1, charPath);
      strPath1 := charPath;
      SHGetPathFromIDList(pidlItem.dwItem2, charPath);
      strPath2 := charPath;
      Memo1.Lines.Add(SHEventName(strPath1, strPath2, Message.lParam) + chr(13)
          + chr(10));
    end;
    
    end.
    View Code

    文件监控

    unit Unit1;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, ExtCtrls, shlobj, Activex;
    
    const
      WM_SHNOTIFY = $401;
    
    type
      TForm1 = class(TForm)
        Panel1: TPanel;
        Panel2: TPanel;
        Button1: TButton;
        Button2: TButton;
        Button3: TButton;
        MM: TMemo;
        procedure FormCreate(Sender: TObject);
        procedure FormClose(Sender: TObject; var Action: TCloseAction);
        procedure Button1Click(Sender: TObject);
        procedure Button2Click(Sender: TObject);
        procedure Button3Click(Sender: TObject);
      private { Private declarations }
        procedure WMShellReg(var Message: TMessage); message WM_SHNOTIFY;
      public { Public declarations }
      end;
    
    type
      PIDLSTRUCT = ^IDLSTRUCT;
    
      _IDLSTRUCT = record
        pidl: PItemIDList;
        bWatchSubFolders: Integer;
      end;
    
      IDLSTRUCT = _IDLSTRUCT;
    
    type
      PSHNOTIFYSTRUCT = ^SHNOTIFYSTRUCT;
    
      SHNOTIFYSTRUCT = record
        dwItem1: PItemIDList;
        dwItem2: PItemIDList;
      end;
    
      // 注册通知消息
    function RegSHNotify(hWnd: Integer): Bool;
    // 解除通知注册
    function UnregSHNotify: Bool;
    // 获取消息具体内容
    function NotifyReceipt(wParam: wParam; lParam: lParam): string;
    // 定义未公开API函数
    Function SHChangeNotifyDeregister(hNotify: Integer): Integer; stdcall;
    external ' Shell32.dll ' index 4;
    Function SHChangeNotifyRegister(hWnd, uFlags, dwEventID, uMSG,
      cItems: LongWord; lpps: PIDLSTRUCT): Integer; stdcall;
    external ' Shell32.dll ' index 2;
    
    var
      Form1: TForm1;
      g_HSHNotify: Integer;
      g_pidlDesktop: PItemIDList;
    
    implementation
    
    {$R *.dfm}
    
    // 获取消息具体内容
    function NotifyReceipt(wParam: wParam; lParam: lParam): string;
    var
      strEvent: String;
      strPath1, strPath2: String;
      szBuf: array [0 .. MAX_PATH] of char;
      pidlItem: PSHNOTIFYSTRUCT;
    begin
      pidlItem := PSHNOTIFYSTRUCT(wParam);
      // 获得系统消息相关的路径
      SHGetPathFromIDList(pidlItem.dwItem1, szBuf);
      strPath1 := szBuf;
      SHGetPathFromIDList(pidlItem.dwItem2, szBuf);
      strPath2 := szBuf;
      // 根据参数设置提示消息
      case lParam of
        SHCNE_RENAMEITEM:
          strEvent := ' 重命名文件: ' + strPath1 + '' + strPath2;
        SHCNE_CREATE:
          strEvent := ' 建立文件, 文件名: ' + strPath1;
        SHCNE_DELETE:
          strEvent := ' 删除文件, 文件名 : ' + strPath1;
        SHCNE_MKDIR:
          strEvent := ' 新建目录, 目录名 : ' + strPath1;
        SHCNE_RMDIR:
          strEvent := ' 删除目录, 目录名 : ' + strPath1;
        SHCNE_ATTRIBUTES:
          strEvent := ' 改变文件目录属性, 文件名 : ' + strPath1;
        SHCNE_MEDIAINSERTED:
          strEvent := strPath1 + ' 中插入可移动存储介质 ';
        SHCNE_MEDIAREMOVED:
          strEvent := strPath1 + ' 中移去可移动存储介质 ';
        SHCNE_DRIVEREMOVED:
          strEvent := ' 移去驱动器: ' + strPath1;
        SHCNE_DRIVEADD:
          strEvent := ' 添加驱动器: ' + strPath1;
        SHCNE_NETSHARE:
          strEvent := ' 改变目录 ' + strPath1 + ' 的共享属性 ';
        SHCNE_UPDATEDIR:
          strEvent := ' 更新目录: ' + strPath1;
        SHCNE_UPDATEITEM:
          strEvent := ' 更新文件, 文件名: ' + strPath1;
        SHCNE_SERVERDISCONNECT:
          strEvent := ' 断开与服务器的连接: ' + strPath1 + ' ' + strPath2;
        SHCNE_UPDATEIMAGE:
          strEvent := ' 更新图标: ' + strPath1 + ' ' + strPath2;
        SHCNE_DRIVEADDGUI:
          strEvent := ' 添加并显示驱动器: ' + strPath1;
        SHCNE_RENAMEFOLDER:
          strEvent := ' 重命名文件夹: ' + strPath1 + '' + strPath2;
        SHCNE_FREESPACE:
          strEvent := ' 磁盘空间大小改变: ' + strPath1 + ' ' + strPath2;
        SHCNE_ASSOCCHANGED:
          strEvent := ' 改变文件关联 ' + strPath1 + ' ' + strPath2;
      else
        strEvent := ' 其他操作 ' + IntToStr(lParam);
      end;
      Result := strEvent;
    end;
    
    // 注册通知消息
    function RegSHNotify(hWnd: Integer): Bool;
    var
      ps: PIDLSTRUCT;
    begin
      Result := False;
      If g_HSHNotify = 0 then
      begin
        // 取得桌面的IDL
        SHGetSpecialFolderLocation(0, CSIDL_DESKTOP
          { CSIDL_DRIVES } , g_pidlDesktop);
        // if SHGetSpecialFolderLocation(0, CSIDL_DESKTOP,g_pidlDesktop)<> NOERROR then
        // Form1.close;
        if Boolean(g_pidlDesktop) then
        begin
          getmem(ps, sizeof(IDLSTRUCT));
          ps.bWatchSubFolders := 1;
          ps.pidl := g_pidlDesktop;
          // 注册Windows监视
          g_HSHNotify := SHChangeNotifyRegister(hWnd, (SHCNF_TYPE Or SHCNF_IDLIST),
            (SHCNE_ALLEVENTS Or SHCNE_INTERRUPT), WM_SHNOTIFY, 1, ps);
          Result := Boolean(g_HSHNotify);
        end
        else
          // 如果出现错误就使用 CoTaskMemFree函数来释放句柄
          CoTaskMemFree(g_pidlDesktop);
      end;
    end;
    // 解除通知注册
    function UnregSHNotify: Bool;
    begin
      Result := False;
      if Boolean(g_HSHNotify) Then
      begin
        // 取消系统消息监视,同时释放桌面的IDL
        if Boolean(SHChangeNotifyDeregister(g_HSHNotify)) Then
        begin
          g_HSHNotify := 1;
          CoTaskMemFree(g_pidlDesktop);
          // Boolean(g_pidlDesktop) :=0;
          Result := True;
        end;
      end;
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      g_HSHNotify := 0;
      MM.Lines.Clear;
      if RegSHNotify(Handle) then
      begin
        MM.Lines.Add('开始监视程序-->成功!');
        Button1.Enabled := False;
      end
      else
        MM.Lines.Add('开始监视程序-->失败!');
    end;
    
    procedure TForm1.Button2Click(Sender: TObject);
    begin
      if Boolean(g_pidlDesktop) then
      begin
        if UnregSHNotify then
        begin
          MM.Lines.Add('停止监视程序-->成功!');
          Button1.Enabled := True;
        end
        else
          MM.Lines.Add('停止监视程序-->失败!');
      end;
    end;
    
    procedure TForm1.Button3Click(Sender: TObject);
    begin
      MessageBox(0, '文件监视功能演示' + #13#10 + 'Coded By: hnxyy' + #13#10 +
          'Homepage: http://www.wrsky.com' + #13#10 + 'Contact: QQ:19026695',
        '火狐出品', 0);
    end;
    
    procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
      // 在程序退出的同时删除监视
      if Boolean(g_pidlDesktop) then
        UnregSHNotify;
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      Caption := Application.Title;
    end;
    
    procedure TForm1.WMShellReg(var Message: TMessage);
    begin
      MM.Lines.Add(NotifyReceipt(Message.wParam, Message.lParam));
      // +chr(13)+chr(10));
    end;
    
    end.
    View Code
  • 相关阅读:
    poj 1222 EXTENDED LIGHTS OUT (高斯消元 )
    poj 2187 Beauty Contest (凸包: 最远点对,最长直径 , 旋转卡壳法)
    poj 1408 Fishnet (几何:线段相交 + 叉积 求面积 )
    poj 1228 Grandpa's Estate ( 凸包 )
    高斯消元 模版
    poj 1830 开关问题 (高斯消元 )
    poj 1113 Wall (凸包:周长)
    旋转卡壳算法
    poj 1681 Painter's Problem (高斯消元 )
    字符串相关处理
  • 原文地址:https://www.cnblogs.com/blogpro/p/11345384.html
Copyright © 2011-2022 走看看