Page 1 of 1

Problem with file notification handles...

Posted: Sat Aug 02, 2003 10:29 pm
by Hi-Toro
OK, I'm trying to monitor a directory for changes -- files created, deleted, etc. This code (below) works if you're only monitoring one folder; however, if you try to monitor more than one, it fails with 'invalid file handle'. Try running it, and copy a file into C: (if you wait more than 10 seconds, the program ends, so be quick!). Now enable the extra AddNotifier lines (altering paths to suit your system) -- it fails.

The program basically creates a list of handles, then passes them (@mylist ()) to the API call WaitForMultipleObjects (), which says to pass an array of handles. As far as I'm aware, @mylist () should be compatible (it's just a block of memory containing 'long' handles, AFAIK), but it fails. Any ideas why?

Code: Select all


#WAIT_FAILED = $FFFFFFFF
#WAIT_OBJECT_0  = $0
#WAIT_ABANDONED = $80
#WAIT_TIMEOUT = $102

Structure Handles
    notifier.l
EndStructure

Global HCount
NewList changes.Handles ()

Procedure AddNotifier (dir$, recursive)
    If flags = 0
        flags = #FILE_NOTIFY_CHANGE_FILE_NAME | #FILE_NOTIFY_CHANGE_DIR_NAME | #FILE_NOTIFY_CHANGE_LAST_WRITE
    EndIf
    handle = FindFirstChangeNotification_ (dir$, recursive, flags)
    If handle <> #INVALID_HANDLE_VALUE
        AddElement (changes ())
        changes ()\notifier = handle
        HCount = HCount + 1
    EndIf
EndProcedure

Procedure DeleteAllNotifiers ()
    While NextElement (changes ())
        FindCloseChangeNotification_ (changes ()\notifier)
        DeleteElement (changes ())
        HCount = HCount - 1
        SelectElement (changes (), LastElement (changes ()))
    Wend
EndProcedure

;Procedure DeleteNotifier (handle)
;    While NextElement (changes ())
;        If changes ()\notifier = handle
;            FindCloseChangeNotification_ (changes ()\notifier)
;            DeleteElement (changes ())
;            HCount = HCount - 1
;            SelectElement (changes (), LastElement (changes ()))
;        EndIf
;    Wend
;EndProcedure

Procedure InitErrors (def)
  SetLastError_ (def)
EndProcedure

; SHOWERROR (title$)

; Puts up a dialog box with last error message, only if an error
; has occurred. The title$ parameter appears in the title bar of
; the dialog box.

Procedure.s ShowError (title$)
  error = GetLastError_ ()
  If error
    AllocateMemory (0, 255)
    FormatMessage_ (#FORMAT_MESSAGE_FROM_SYSTEM, #NULL, error, 0, MemoryID (), 255, #NULL)
    e$ = PeekS (MemoryID ())
    FreeMemory (0)
    MessageRequester (title$, e$, #MB_ICONWARNING) ; ProcedureReturn e$ ; Alternative to MessageRequester!
  EndIf
EndProcedure

; T e s t . . .

InitErrors (0)

msg$ = "Copy something anywhere in C: after closing this requester! (Program"
msg$ + Chr (10) + " will end in 10 seconds if you do nothing, or 10 secs after last file operation.)"
msg$ + Chr (10) + "Then try enabling the following AddNotifier lines..."
MessageRequester ("Info", msg$, 0)

; UNCOMMENT THESE! CHANGE TO SUIT YOUR SYSTEM...

AddNotifier ("C:\", #TRUE)
;AddNotifier ("D:\", #TRUE)
;AddNotifier ("T:\", #TRUE)

ResetList (changes ())
While NextElement (changes ())
    Debug "Watching " + Str (changes ()\notifier)
Wend

FirstElement (changes ())

Repeat

    result = WaitForMultipleObjects_ (HCount, @changes (), #FALSE, 10000)

    ; Valid result! See WaitForMultipleObjects docs to explain bounds check!
    
    If result => #WAIT_OBJECT_0 And result <= #WAIT_OBJECT_0 + HCount - 1

        Debug "Change in monitored folders/sub-folders!"

        ; Reset the handle that caused the notification (again, see docs)...

        notifier = #WAIT_OBJECT_0 - result
        SelectElement (changes (), notifier)
        FindNextChangeNotification_ (changes ()\notifier)

    Else
    
        ; Result failed!
        
        If result = #WAIT_FAILED
            ShowError ("WaitForMultipleObjects result: #WAIT_FAILED")      ; Handle invalid?!!
            DeleteAllNotifiers ()
            End
        EndIf

    EndIf

Until result = #WAIT_TIMEOUT ; Programs ends after 10 seconds...

DeleteAllNotifiers ()

End


Posted: Thu Jan 22, 2004 5:49 am
by Dare2
Hi,

Just wondering how you went with this?

Do you also have a way of determining where incoming files came from?

I want to monitor my "Temporary Internet Files" folder and try to catch the app or site that is setting up gator and assoc cookies & files, and then attempting to run an activeX control and install gator.

So far I can't find it, so thought perhaps I could catch it using something along these lines.

Posted: Thu Jan 22, 2004 8:43 am
by Hi-Toro
Hi Dare,

I don't think I ever quite figured it out, but did get this 'file monitor' working -- note that it's two separate source files

; "filemon-incs.pb"

Code: Select all

; PureBasic Visual Designer v3.72


;- Window Constants
;
#Window_0 = 0

;- Gadget Constants
;
#Gadget_1 = 0
#Gadget_0 = 1
#Gadget_2 = 2
#Gadget_3 = 3
#Gadget_4 = 4
#Gadget_5 = 5
#Gadget_6 = 6


Procedure Open_Window_0()
  If OpenWindow(#Window_0, 0, 0, 440, 410,  #PB_Window_SystemMenu | #PB_Window_TitleBar | #PB_Window_ScreenCentered , "New File Monitor - Hi-Toro 2003 (Public Domain: spread freely!)")
    If CreateGadgetList(WindowID())
      ListIconGadget(#Gadget_1, 20, 30, 400, 130, "Choose drives to monitor", 400 - (GetSystemMetrics_ (#SM_CXEDGE) * 2), #PB_ListIcon_CheckBoxes)
      Frame3DGadget(#Gadget_0, 10, 10, 420, 160, "Drives to monitor")
      Frame3DGadget(#Gadget_2, 10, 180, 420, 160, "Files added to selected drives during monitoring")
      ListViewGadget(#Gadget_3, 20, 200, 400, 130)
      ButtonGadget(#Gadget_4, 10, 350, 160, 30, "Monitor selected drives")
      ButtonGadget(#Gadget_5, 180, 350, 160, 30, "Save list of files added...")
      ButtonGadget(#Gadget_6, 350, 350, 80, 30, "Reset")
      CreateStatusBar (0, WindowID ())
    EndIf
  EndIf
EndProcedure
; filemon.pb

Code: Select all

; PureBasic Visual Designer v3.72

IncludeFile "filemon-incs.pbi"

Open_Window_0()

DisableGadget (#Gadget_5, 1)

Procedure GetDriveList ()

    Structure Drives
        name.s
    EndStructure

    NewList disk.Drives ()

    ResetList (disk ())

    ; Stupid, stupid Windows...

    AllocateMemory (0, 255)
    result = GetLogicalDriveStrings_ (255, MemoryID ())
    If result > 255
        ReAllocateMemory (0, result)
        GetLogicalDriveStrings_ (result, MemoryID ())
    EndIf

    If result

        For a = 0 To result - 1
            byte = PeekB (MemoryID () + a)
            If byte
                drive$ = drive$ + Chr (byte)
            Else
                AddElement (disk ())
                disk ()\name = drive$
                drive$ = ""
            EndIf
        Next

        FreeMemory (0)
        ProcedureReturn #TRUE

    Else
        
        FreeMemory (0)
        ProcedureReturn #FALSE
        
    EndIf

EndProcedure

If GetDriveList ()

    ResetList (disk ())
    While NextElement (disk ())
        AddGadgetItem (#Gadget_1, -1, disk ()\name)
    Wend

EndIf

Structure FileList
    entry.s
    drive.s
EndStructure

NewList files.FileList ()

Procedure CountFiles (dir$, recursecount, drive$)

    If Right (dir$, 1) <> "\"
        dir$ = dir$ + "\"
    EndIf

    If ExamineDirectory (recursecount, dir$, "*.*")
      
      Repeat
      
        entry = NextDirectoryEntry ()
        file$ = dir$ + DirectoryEntryName ()
        
        ; File...
        
        If entry = 1
            AddElement (files ())
            files ()\entry = file$
            files ()\drive = drive$
        Else
        
          ; Folder...
          
          If entry = 2
            folder$ = DirectoryEntryName ()
            If folder$ <> "." And folder$ <> ".."
                If Right (folder$, 1) <> "\"
                    folder$ = folder$ + "\"
                EndIf
                folder$ = dir$ + folder$
                AddElement (files ())
                files ()\entry = folder$
                files ()\drive = drive$
                CountFiles (folder$, recursecount + 1, drive$)
                UseDirectory (recursecount)
            EndIf
          EndIf
        EndIf
        
      Until entry = 0
      
  EndIf

EndProcedure

NewList files2.FileList ()

NewList diffs.FileList ()

Procedure CountFiles2 (dir$, recursecount, drive$)

    If Right (dir$, 1) <> "\"
        dir$ = dir$ + "\"
    EndIf

    If ExamineDirectory (recursecount, dir$, "*.*")
      
      Repeat
      
        entry = NextDirectoryEntry ()
        file$ = dir$ + DirectoryEntryName ()
        
        ; File...
        
        If entry = 1
            AddElement (files2 ())
            files2 ()\entry = file$
            files2 ()\drive = drive$
        Else
        
          ; Folder...
          
          If entry = 2
            folder$ = DirectoryEntryName ()
            If folder$ <> "." And folder$ <> ".."
                If Right (folder$, 1) <> "\"
                    folder$ = folder$ + "\"
                EndIf
                folder$ = dir$ + folder$
                AddElement (files2 ())
                files2 ()\entry = folder$
                files2 ()\drive = drive$
                CountFiles2 (folder$, recursecount + 1, drive$)
                UseDirectory (recursecount)
            EndIf
          EndIf
        EndIf
        
      Until entry = 0
      
  EndIf

EndProcedure

Structure SelectedDisk
    name.s
EndStructure

NewList selected.SelectedDisk ()

#WAIT_FAILED = $FFFFFFFF 
#WAIT_OBJECT_0  = $0 
#WAIT_ABANDONED = $80 
#WAIT_TIMEOUT = $102 

Repeat

    e = WaitWindowEvent ()
    
    Select e

        Case #PB_Event_CloseWindow
            End

        Case #PB_Event_Gadget

            Select EventGadgetID ()

                Case #Gadget_4

                    ClearList (selected ())
                    
                    seld = 0
                    
                    For check = 0 To CountGadgetItems (#Gadget_1) - 1
                        If GetGadgetItemState (#Gadget_1, check) & #PB_ListIcon_Checked
                            AddElement (selected ())
                            selected ()\name = GetGadgetItemText (#Gadget_1, check, 0)
                            seld = seld + 1
                        EndIf
                    Next
                    
                    If seld

                        ClearGadgetItemList (#Gadget_3)

                        DisableGadget (#Gadget_4, #TRUE)
                        StatusBarText (0, 0, "Compiling file list -- please wait...")
                        
                        Dim Handles (seld)
                        
                        ResetList (selected ())
                        hcount = 0
                        While NextElement (selected ())
                            Handles (hcount) = FindFirstChangeNotification_ (selected ()\name, #TRUE, #FILE_NOTIFY_CHANGE_FILE_NAME | #FILE_NOTIFY_CHANGE_DIR_NAME)
                            
                            ClearList (files ())
                            CountFiles (selected ()\name, 0, selected ()\name)
                            
                            hcount = hcount + 1
                        Wend

                        DisableGadget (#Gadget_4, #FALSE)
                        StatusBarText (0, 0, "")

;ResetList (files ())
;While NextElement (files ())
;    Debug files ()\drive + " -- " + files ()\entry
;Wend

                        ResetList (selected ())
                        While NextElement (selected ())
                            selected ()\name
                        Wend
                        
                        If hcount
                        
                            SetGadgetText (#Gadget_4, "Stop monitoring selected drives")

                            DisableGadget (#Gadget_1, #TRUE)
                            DisableGadget (#Gadget_3, #TRUE)
                            DisableGadget (#Gadget_6, #TRUE)
                            
                            changes = 0
                            
                            Repeat
                            
                                e = WindowEvent ()
                                
                                result = WaitForMultipleObjects_ (hcount, @Handles (), #FALSE, 10)
                                
                                ; Valid result! See WaitForMultipleObjects docs to explain bounds check! 
                                
                                If result => #WAIT_OBJECT_0 And result <= #WAIT_OBJECT_0 + hcount - 1 

                                    ;Debug "Change in monitored folders/sub-folders!" 
                                    
                                    changes = changes + 1
                                    
                                    ; Reset the handle that caused the notification (again, see docs)... 

                                    FindNextChangeNotification_ (Handles (result - #WAIT_OBJECT_0)) 

                                Else 
                                
;                                    ; Result failed! 
;                                    
                                    If result = #WAIT_FAILED
                                    
                                        MessageRequester ("New file monitor", "One or more selected drives unavailable!", #MB_ICONWARNING)
                                        
                                        For check = 0 To CountGadgetItems (#Gadget_1) - 1
                                            SetGadgetItemState (#Gadget_1, check, 0)
                                        Next

                                        ClearGadgetItemList (#Gadget_1)
                                        ClearGadgetItemList (#Gadget_3)
                                        
                                        ClearList (disk ())                    
                                        
                                        If GetDriveList ()
                                            ResetList (disk ())
                                            While NextElement (disk ())
                                                AddGadgetItem (#Gadget_1, -1, disk ()\name)
                                            Wend
                                        EndIf
                                        
                                    EndIf 

                                EndIf 

                            Until e = #PB_EventGadget Or result = #WAIT_FAILED

                            For a = 0 To seld - 1
                                FindCloseChangeNotification_ (Handles (a))
                            Next
                                             
                            DisableGadget (#Gadget_1, #FALSE)
                            DisableGadget (#Gadget_3, #FALSE)
                            DisableGadget (#Gadget_6, #FALSE)

                            SetGadgetText (#Gadget_4, "Monitor selected drives")
                        
                            If changes

                                ResetList (selected ())
                                While NextElement (selected ())
                                    ClearList (files2 ())
                                    CountFiles2 (selected ()\name, 0, selected ()\name)
                                Wend
                                
                                ClearList (diffs ())
                                ResetList (files ())

                                While NextElement (files ())
                                    ResetList (files2 ())
                                    While NextElement (files2 ())
                                        If files ()\entry = files2 ()\entry
                                            DeleteElement (files2 ())
                                            LastElement (files2 ())
                                        EndIf
                                    Wend
                                Wend
                                
                                ResetList (files2 ())
                                While NextElement (files2 ())
                                    If FileSize (files2 ()\entry) <> -1
                                        AddGadgetItem (#Gadget_3, -1, files2 ()\entry)
;                                        Debug files2 ()\drive + " -- " + files2 ()\entry
                                    EndIf
                                Wend

                            EndIf
                            
                        EndIf
                        
                    EndIf

                Case #Gadget_6
                
                    For check = 0 To CountGadgetItems (#Gadget_1) - 1
                        SetGadgetItemState (#Gadget_1, check, 0)
                    Next

                    ClearGadgetItemList (#Gadget_1)
                    ClearGadgetItemList (#Gadget_3)
                    
                    ClearList (disk ())                    
                    
                    If GetDriveList ()
                        ResetList (disk ())
                        While NextElement (disk ())
                            AddGadgetItem (#Gadget_1, -1, disk ()\name)
                        Wend
                    EndIf
                    
            EndSelect

    EndSelect
    
ForEver
Although you can determine which files have been added using some NT-specific APIs, there's no way to determine what's new on 9x without comparing the before-and-after state as far as I'm aware (so that's what this program does)...

Finding out which program owns a file is pretty advanced, low-level stuff I've never figured out (have a look at www.sysinternals.com for their 'handle' or 'Process Explorer' programs, which can do this, though they don't explain how)...

I can't really remember exactly how this program works now!

Posted: Thu Jan 22, 2004 10:09 am
by Dare2
Thanks for posting that, Hi-Toro.

Also thanks for the pointer as to where to look.

The first bit of code you posted did okay checking one folder (which is all I am after at the moment). But this looks like a bit of an advance, so I'll rip your code and play with it if you don't mind.

Success to you. :)