Jump to content

FileSystemMonitor UDF


seangriffin
 Share

Recommended Posts

  • 2 weeks later...

Hi!

As far as I can see, the UDF it does not support subdirectories, though. Or did I misunderstand something? In my test with the provided example "FileSystemMonitor example.au3" I could not see that it would support subdirectories.

What would be the best way to do that?

Thx for everything so far. Great work!!!

Regards, Stephan

Link to comment
Share on other sites

Hi once more,

The issue above may be considered only as a feature on the wishlist. But now I found 2 real bugs:

1) The "system wide" shell monitoring when specified without a path is not really system wide. It does not realize changes to the root-directory of a drive (i.e "C:\"). When i.e. I add or delete a file to "C:\path1\" then an event of the type "Folder of non-folder has changed" is monitored by the shell monitor. But when I do the same directly on "C:\" nothing is monitored by the shell monitor.

A temporary solution could be to use the dir monitor directly on "C:\", which, however, leads to the second bug:

2) This "temporary solution" does not work if I have more than one drive that I want to monitor, since I could not find out how to successfully monitor more than one directory at the same time. When I tried, it at first only monitored the last path specified and eventually the script crashed anyway. Maybe this second bug is also more a wishlist-feature. With the shell monitor it also is not as important since I could monitor "everything" (if bug #1 was not here) and then filter the events. But for the dir monitor this would really be very useful. Is it possible somehow?

Thanx, Stephan

Link to comment
Share on other sites

Hi, it's me again.

Sorry for all the bugging :idea:, but here is another "half" a bug / "half" a wishlist-item:

When a folder (or a file) is renamed the shell monitor returns only the "old" and already outdated path (or filename). This can often be not enough information, because the new path (or filename) is much more helpful, I think. Best would be to get returned both, of course.

Kind regards, Stephan

Link to comment
Share on other sites

Hi Stephan ...

Did you see what forum you were in?

* > Example Scripts

The script works as intended -- an example.

If you want it to do all those things, then you'll be the one that can modify it the best!

-Edit-

Speaking of modifying .. here's my slimmed down version. It monitors a specific

folder for file and folder changes. Create/Delete/Rename. Without system wide

shell notifications. Added logging to file with a few other changes.

Folder_Monitor.au3

Edited by ripdad

"The mediocre teacher tells. The Good teacher explains. The superior teacher demonstrates. The great teacher inspires." -William Arthur Ward

Link to comment
Share on other sites

Hi Stephan ...

Did you see what forum you were in?

* > Example Scripts

The script works as intended -- an example.

If you want it to do all those things, then you'll be the one that can modify it the best!

Hi ripdad,

thx for the response. I am sorry that my words obviously sounded too much like a demand or so. I did not want to put any undue pressure on anybody or make anybody feel uncomfortable. Just wanted to give a few thoughts. If I can find out how to implement some improvements and find the time than I would like to do that. On the other hand I want to make sure that I do not interfere with the original creator of this script, especially since he made the effort with that official (?) UDF stuff. (Not sure, though, if I understand completely what is involved with the UDF stuff.)

Any ideas / hints for how to implement my "thoughts" are welcome :idea:

Kind regards, Stephan

Link to comment
Share on other sites

Stephan,

To be forthcoming ...

With all my testing on shell events, I noticed it's not a total 100% return in some cases.

For example - If I delete 100 files in a folder, it might miss a few or even up to half of them.

It might be okay for a half a dozen files consistently.

Anyways, I'd be hard pressed to trust it in a critical situation.

It might be better to load the folder contents into an array and then poll it every so often

for changes. I haven't played with that yet -- but I'm sure it's doable.

As for your ideas .. they can be done, I'm sure. There are a couple of examples of monitoring

more than one folder on the forum.

You might want to follow some of the other threads mentioned on the first page - if you haven't already.

As far as the script crashing .. it shouldn't, since I think we worked all the crash bugs out.

What OS are you using?

-edit spelling-

Edited by ripdad

"The mediocre teacher tells. The Good teacher explains. The superior teacher demonstrates. The great teacher inspires." -William Arthur Ward

Link to comment
Share on other sites

  • 2 weeks later...

@seangriffin:

Thank you for sharing. It works fine! :mellow:

The function "_FileSysMonSetup()" should return either False or True.

I can see only "Return True": In which case will this function return False?

Greets,

-supersonic.

Edited by supersonic
Link to comment
Share on other sites

  • 2 months later...

Another great UDF, seangriffin.

I'm wondering if it somehow AT ALL also is possible to see what files are being read (and by what)?

For example, an application is opening C:\temp.txt, and simply closes it - any way to detect?

Link to comment
Share on other sites

  • 1 month later...

First of all thanks for this very useful and good explaneid UDF.

I have found strange behaviour, I (lazy) have altered your example for monitor when appears a certain file in network drive (x:\ I mean)

I have found that when a particular program is launched (dike.exe, in Italy is the government digital signature program) this crashes your example altered by me but not crashes your original example, see the attached image.

You are right if you think that my alterations are the cause, but the strange fact is that I have resolved with a "sleep (1000)".

The code below is straight from your example (but I filter certain events) until the "sleep" instruction.

If StringLen($fs_event.item(Hex($event_id))) > 0 Then

        $event_type_name = StringSplit($fs_event.item(Hex($event_id)), "|")
        $event_type_name[2] = $event_type_name[2] & "(" & $event_id & ")"

        If StringInStr($event_type_name[1], "folder") <> 0 And StringInStr($event_type_name[1], "drive") Then
            _GUICtrlListView_InsertItem($listview, $event_type_name[1], 0)
            _GUICtrlListView_SetItemText($listview, 0, $event_type_name[2], 1)
            _GUICtrlListView_SetItemText($listview, 0, $event_value, 2)
            _GUICtrlListView_SetItemText($listview, 0, @HOUR & ":" & @MIN & ":" & @SEC, 3)
        EndIf
    EndIf
    
    sleep (1000) ;  ; --> this saves from crash ??
    ;file actions starts here
    If $event_type_name[1] = "file added to the directory" Then
        processTF($event_value)
    EndIf

Any ideas ?

post-43241-12833447505173_thumb.jpg

Link to comment
Share on other sites

Hi!

As far as I can see, the UDF it does not support subdirectories, though. Or did I misunderstand something? In my test with the provided example "FileSystemMonitor example.au3" I could not see that it would support subdirectories.

What would be the best way to do that?

Thx for everything so far. Great work!!!

Regards, Stephan

I tried to change this line:

$ret = DllCall("kernel32.dll", "Int", "ReadDirectoryChangesW", "hwnd", $hDir, "ptr", $pBuffer, "dword", $iBufferSize, "int", False, "dword", BitOR(0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x100), "Uint", 0, "Uint", $pOverLapped, "Uint", 0)

to

$ret = DllCall("kernel32.dll", "Int", "ReadDirectoryChangesW", "hwnd", $hDir, "ptr", $pBuffer, "dword", $iBufferSize, "int", True, "dword", BitOR(0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x100), "Uint", 0, "Uint", $pOverLapped, "Uint", 0)

Now this problem solved.

Link to comment
Share on other sites

  • 2 months later...
  • 7 months later...

I modified "FileSystemMonitor Explorer example.au3":

#include <GUIConstants.au3>
#include <GUIConstantsEx.au3>
#include <EditConstants.au3>
#include <ListviewConstants.au3>
#Include <GuiListView.au3>
#Include <GuiListBox.au3>
#include <WindowsConstants.au3>
#Include <File.au3>
#include <FileSystemMonitor.au3>
#NoTrayIcon
dim $msg
;Global $old_item, $directory_listview, $drive_list, $path_input, $deleteitem, $renameitem, $old_name
Global $old_item, $directory_listview, $path_input, $old_name

; Get the list of drives
;$drive = DriveGetDrive("all")
;_ArrayDelete($drive, 0)
;$drives = StringUpper(StringReplace(_ArrayToString($drive), ":", ""))

; Create GUI
;$main_gui = GUICreate("FileSystemMonitor Explorer Example (Press ESC to close)", 400, 320, -1, -1, -1, $WS_EX_TOPMOST)
;$main_gui = GUICreate($cmdLine[2], 180, 320, 75, 0, -1, $WS_EX_TOOLWINDOW + $WS_EX_TOPMOST)
$main_gui = GUICreate($cmdLine[2], 180, 100, 0, @DesktopHeight-100-80, -1, $WS_EX_TOOLWINDOW + $WS_EX_TOPMOST)
;GUICtrlCreateLabel("Directory:", 10, 10, 50)
;$path_input = GUICtrlCreateInput("\Scripts\Cygwin_Scripts\tmp\", 70, 10, 320, 20, $ES_READONLY)
;$path_input = GUICtrlCreateInput($cmdLine[1], 10, 10, 160, 20, $ES_READONLY)
$path_input = $cmdLine[1]
;GUICtrlCreateLabel("Drives", 10, 40, 50)
;$drive_list = GUICtrlCreateList("", 10, 60, 40, 230)
;GUICtrlSetData($drive_list, $drives)
;GUICtrlCreateLabel("Directory Listing", 190, 40, 80)
;$directory_listview = _GUICtrlListView_Create($main_gui, "", 70, 60, 320, 230, BitOR($LVS_EDITLABELS, $LVS_REPORT))
$directory_listview = _GUICtrlListView_Create($main_gui, "", 10, 0, 160, 80, BitOR($LVS_EDITLABELS=0, $LVS_REPORT))
;_GUICtrlListView_InsertColumn($directory_listview, 0, "Name", 300)
_GUICtrlListView_InsertColumn($directory_listview, 0, $cmdLine[3], 140)
;$scroll_checkbox = GUICtrlCreateCheckbox("Automatically scroll to new and changed items", 70, 295, 300)
$scroll_checkbox = GUICtrlCreateCheckbox("Automatically scroll", 30, 80, 260)
GUICtrlSetState($scroll_checkbox, $GUI_CHECKED)
;$deleteitem = GUICtrlCreateDummy()
;$renameitem = GUICtrlCreateDummy()
GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")
;dim $main_gui_accel[2][2]=[["{DELETE}", $deleteitem], ["{F2}", $renameitem]]

; Setup File System Monitoring
_FileSysMonSetup(3, $cmdLine[1], "")
UpdatePathListingMonitor($cmdLine[1])

; Show GUI
GUISetState(@SW_SHOW)
;GUISetAccelerators($main_gui_accel)
;_GUICtrlListView_ClickItem($directory_listview, 0)     PROBLEMATIC

; Main Loop
While 1

    ; Handle Directory related events
    _FileSysMonDirEventHandler()

    ;If $msg = $drive_list then 
        
    ;   UpdatePathListingMonitor(GUICtrlRead($drive_list) & ":\")
    ;EndIf

    ;If $msg = $renameitem then
        
    ;   $old_name = GUICtrlRead($path_input) & StringReplace(_GUICtrlListView_GetItemTextString($directory_listview), "<DIR> ", "")
    ;   _GUICtrlListView_EditLabel($directory_listview, int(_GUICtrlListView_GetSelectedIndices($directory_listview)))
    ;EndIf

    ;if $msg = $deleteitem Then

    ;   $old_name = GUICtrlRead($path_input) & StringReplace(_GUICtrlListView_GetItemTextString($directory_listview), "<DIR> ", "")
    ;   $ans = MsgBox(262148, "FileSystemMonitor Explorer - Recycle", "Do you want to recycle:" & @crlf & @crlf & $old_name)
        
    ;   if $ans = 6 Then
        
    ;       _GUICtrlListView_DeleteItem($directory_listview, int(_GUICtrlListView_GetSelectedIndices($directory_listview)))
    ;       FileRecycle($old_name)
    ;   EndIf
    ;EndIf

    ;If FileExists($path_input) and StringInStr(FileGetAttrib($path_input),"D") Then
    If Not FileExists($path_input) and Not StringInStr(FileGetAttrib($path_input),"D") Then
        ;MsgBox(0,"Folder Not Found","The folder " & $path_input & " does not exists")
        ExitLoop
    EndIf

    If $msg = $GUI_EVENT_CLOSE then 
        
        ExitLoop
    EndIf

    $msg = GUIGetMsg()
WEnd

Func _FileSysMonActionEvent($event_type, $event_id, $event_value)

    ConsoleWrite($event_type & "|" & $event_id & "|" & $event_value & @CRLF)
    
    Switch $event_type
    
        case 0
    
            Switch $event_id

                Case 0x00000001 ; file / folder added
                
                    ConsoleWrite("adding file / folder " & $event_value & @CRLF)
                    
                    ; If the item is a folder
                    ;if StringInStr(FileGetAttrib(GUICtrlRead($path_input) & $event_value), "D") > 0 Then $event_value = "<DIR> " & $event_value
                    if StringInStr(FileGetAttrib($path_input & $event_value), "D") > 0 Then $event_value = "<DIR> " & $event_value
                    
                    $new_index = ListViewGetSortedIndex($directory_listview, $event_value)
                    _GUICtrlListView_InsertItem($directory_listview, $event_value, $new_index)
                    
                    if GUICtrlRead($scroll_checkbox) = $GUI_CHECKED Then
                    
                        _GUICtrlListView_EnsureVisible($directory_listview, $new_index)
                        _GUICtrlListView_SetItemFocused($directory_listview, $new_index)
                    EndIf

                Case 0x00000002 ; file removed
                
                    ConsoleWrite("removing file " & $event_value & @CRLF)
                    _GUICtrlListView_DeleteItem($directory_listview, _GUICtrlListView_FindText($directory_listview, $event_value, -1, False))

                Case 0x00000004 ; file / folder renamed - old name
                    
                    $old_item = $event_value

                Case 0x00000005 ; file / folder renamed - new name

                    ConsoleWrite("renaming file " & $old_item & " to " & $event_value & @CRLF)

                    ; If the item is a folder
                    ;if StringInStr(FileGetAttrib(GUICtrlRead($path_input) & $event_value), "D") > 0 Then 
                    if StringInStr(FileGetAttrib($path_input & $event_value), "D") > 0 Then 

                        $old_item = "<DIR> " & $old_item
                        $event_value = "<DIR> " & $event_value
                    EndIf

                    _GUICtrlListView_DeleteItem($directory_listview, _GUICtrlListView_FindText($directory_listview, $old_item, -1, False))
                    
                    if _GUICtrlListView_FindText($directory_listview, $event_value) = -1 Then
                    
                        $new_index = ListViewGetSortedIndex($directory_listview, $event_value)
                        _GUICtrlListView_InsertItem($directory_listview, $event_value, $new_index)
                    
                        if GUICtrlRead($scroll_checkbox) = $GUI_CHECKED Then
                            
                            _GUICtrlListView_EnsureVisible($directory_listview, $new_index)
                            _GUICtrlListView_SetItemFocused($directory_listview, $new_index)
                        EndIf
                    EndIf
            EndSwitch
            
        case 1

            Switch $event_id

                Case 0x00000010 ; folder removed

                    ConsoleWrite("removing folder " & $event_value & @CRLF)
                    $event_value = "<DIR> " & StringMid($event_value, StringInStr($event_value, "\", 0, -1) + 1)
                    _GUICtrlListView_DeleteItem($directory_listview, _GUICtrlListView_FindText($directory_listview, $event_value, -1, False))

                ;Case 0x00000100    ; drive added
                
                ;   ConsoleWrite("adding drive " & $event_value & @CRLF)
                ;   GUICtrlSetData($drive_list, StringReplace(StringReplace($event_value, ":", ""), "\", ""))
                
                ;case 0x00000080    ; drive removed
                
                ;   ConsoleWrite("removing drive " & $event_value & @CRLF)
                ;   $event_value = StringReplace(StringReplace($event_value, ":", ""), "\", "")
                    
                ;   if StringCompare($event_value, GUICtrlRead($drive_list)) = 0 Then
                        
                ;       _GUICtrlListBox_SelectString($drive_list, "C")
                ;       UpdatePathListingMonitor("\Scripts\Cygwin_Scripts\tmp\")
                ;   EndIf
                    
                ;   _GUICtrlListBox_DeleteString($drive_list, _GUICtrlListBox_FindString($drive_list, $event_value, True))
            EndSwitch
    EndSwitch
EndFunc

Func ListViewGetSortedIndex($hWnd, $vValue)

    Local $iStart = 0, $iEnd = 0, $iUBound = _GUICtrlListView_GetItemCount($hWnd) - 1

    $iEnd = $iUBound
    Local $iMid = Int(($iEnd + $iStart) / 2)

    ; Search
    While $iStart <= $iMid And $vValue <> _GUICtrlListView_GetItemText($hWnd, $iMid)
        
        If $vValue < _GUICtrlListView_GetItemText($hWnd, $iMid) Then
            $iEnd = $iMid - 1
        Else
            $iStart = $iMid + 1
        EndIf
        $iMid = Int(($iEnd + $iStart) / 2)
    WEnd

    Return ($iMid + 1)
EndFunc

Func WM_NOTIFY($hWnd, $iMsg, $iwParam, $ilParam)
    
    #forceref $hWnd, $iMsg, $iwParam
    Local $hWndFrom, $iIDFrom, $iCode, $tNMHDR, $hWndListView, $tInfo
    $hWndListView = $directory_listview
    If Not IsHWnd($directory_listview) Then $hWndListView = GUICtrlGetHandle($directory_listview)
    $tNMHDR = DllStructCreate($tagNMHDR, $ilParam)
    $hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom"))
    $iIDFrom = DllStructGetData($tNMHDR, "IDFrom")
    $iCode = DllStructGetData($tNMHDR, "Code")
    
    Switch $hWndFrom
        Case $hWndListView
            Switch $iCode
                Case $NM_DBLCLK
                    
                    $tInfo = DllStructCreate($tagNMITEMACTIVATE, $ilParam)
                    
                    ConsoleWrite(DllStructGetData($tInfo, "Index"))
                    
                    $item_text = _GUICtrlListView_GetItemText($directory_listview, DllStructGetData($tInfo, "Index"))
                    
                    if StringInStr($item_text, "<DIR> ") > 0 Then
                    
                        if StringCompare($item_text, "<DIR> ..") = 0 Then
                            
                            ;$new_path = StringLeft(GUICtrlRead($path_input), StringInStr(GUICtrlRead($path_input), "\", 0, -2))
                            $new_path = $cmdLine[1]
                        Else
                    
                            ;$new_path = GUICtrlRead($path_input) & StringReplace($item_text, "<DIR> ", "") & "\"
                            $new_path = $path_input & StringReplace($item_text, "<DIR> ", "") & "\"
                        EndIf
                        
                        UpdatePathListingMonitor($new_path)
                    EndIf
                
                Case $LVN_ENDLABELEDITA, $LVN_ENDLABELEDITW ; The end of label editing for an item

                    $tInfo = DllStructCreate($tagNMLVDISPINFO, $ilParam)
                    Local $tBuffer = DllStructCreate("char Text[" & DllStructGetData($tInfo, "TextMax") & "]", DllStructGetData($tInfo, "Text"))
                    
                    if DllStructGetData($tInfo, "Text") <> 0 Then
                    
                        ;$new_name = GUICtrlRead($path_input) & StringReplace(DllStructGetData($tBuffer, "Text"), "<DIR> ", "")
                        $new_name = $path_input & StringReplace(DllStructGetData($tBuffer, "Text"), "<DIR> ", "")
                        
                        if StringInStr(FileGetAttrib($old_name), "D") > 0 Then
                            
                            DirMove($old_name, $new_name)
                        Else
                        
                            FileMove($old_name, $new_name)
                        EndIf
                        
                        _GUICtrlListView_SetItemText($directory_listview, int(_GUICtrlListView_GetSelectedIndices($directory_listview)), DllStructGetData($tBuffer, "Text"))
                    EndIf
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc

Func UpdatePathListingMonitor($path)
    
    ;GUICtrlSetData($path_input, $path)
    
    _GUICtrlListView_BeginUpdate($directory_listview)

    _GUICtrlListView_DeleteAllItems($directory_listview)

    ; folders
    if StringInStr($path, "\", 0, 2) > 0 Then _GUICtrlListView_AddItem($directory_listview, "<DIR> ..")
    $foldername = _FileListToArray($path, "*", 2)
    
    if @error <> 4 and IsArray($foldername) and $foldername[0] > 0 Then
    
        _ArrayDelete($foldername, 0)
        for $each in $foldername

            _GUICtrlListView_AddItem($directory_listview, "<DIR> " & $each)
        Next
    EndIf

    ; files
    $filename = _FileListToArray($path, "*", 1)
    
    if @error <> 4 and IsArray($filename) and $filename[0] > 0 Then
        
        _ArrayDelete($filename, 0)
        for $each in $filename

            _GUICtrlListView_AddItem($directory_listview, $each)
        Next
    EndIf

    _GUICtrlListView_EndUpdate($directory_listview)
;   _GUICtrlListView_ClickItem($directory_listview, 0)      PROBLEMATIC
    
    _FileSysMonSetDirMonPath($path)
    _FileSysMonSetShellMonPath($path)
    
EndFunc

It works like: FileSystemMonitorExplorerUDF.exe c:\WINDOWS\ Title Comment

The only problem I have is when some folder is in-use by this application, if you delete it, it recreates it back.

When you try to access this folder it's access deny.

I have a function to check whether it's root folder exist, and if it's not exist to exit.

It works OK when the folder is deleted by cygwin, the problem appears only with windows/cmd.

Thanks.

Link to comment
Share on other sites

  • 6 months later...

It seems to only work in the root folder not subfolders.

Check the post 3 posts before yours, it suggests a fix for that.

If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Link to comment
Share on other sites

I tried changing False to True. But it still seems to only show files created under subdirectories not changes.

$ret = DllCall("kernel32.dll", "Int", "ReadDirectoryChangesW", "hwnd", $hDir, "ptr", $pBuffer, "dword", $iBufferSize, "int", True, "dword", BitOR(0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x100), "Uint", 0, "Uint", $pOverLapped, "Uint", 0)

Cheers

Link to comment
Share on other sites

  • 2 weeks later...

Would it be possible to monitor multiple directories with this somehow?

I have a list (array) with about 5 or 6 directories that need to check if files with a certain extension have changed and then execute an external program.

I am trying to figure out how to do that and came across this, but do I understand correctly that this can only monitor one directory?

Link to comment
Share on other sites

  • 2 weeks later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...