Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

File system watcher

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • PBWin/PBCC File system watcher

    Here's a variation on Filewatcher that watches a directory for selected changes. Can optionally include sub-folders.

    Base Includes. Needed for all demo's.
    FilesystemWatcher.inc
    Code:
    #Include Once "StringStack.inc"
     
    ' File system changes to watch for
    ' Can be any combination of the following
    Enum fsWatchFor
      Filename      = &H1
      DirectoryName = &H2
      Attributes    = &H4
      Size          = &H8
      Write         = &H10
      Access        = &H20
      Creation      = &H40
      Security      = &H100
      All           = &H17f
    End Enum
     
    ' List of change events sent to notification
    Enum fsEvent
      Added = 1
      Removed = 2
      Modified = 3
      OldName = 4
      NewName = 5
    End Enum
     
     
    %BUFSIZE = 1024
    Type fswBuffer
      buf As StringZ * %BUFSIZE
    End Type
     
    $FSW_CLSID = Guid$("{53DBFFBE-D218-4F82-AD7A-EC375C198E4B}")
    $FSW_IID = Guid$("{B91C1ECC-39E5-4415-AE17-CC9868470758}")
     
    Sub StartFilesystemWatcher Alias "StartFilesystemWatcher" ( obj As iFilesystemWatcher, initialDirectory As WString, ByVal notifyRoutine As Dword, ByVal watchFor As Dword, ByVal watchTree As Long ) Common Export
      obj = Class "cFilesystemWatcher"
      obj.Initialize( initialDirectory, notifyRoutine, watchFor, watchTree )
      obj.Launch( ByVal 0 )
    End Sub
     
    Sub FilesystemWatcherNotifyPattern( filename As WString, ByVal changeEvent As Long )
    End Sub
     
    Class cFilesystemWatcher $FSW_CLSID Common
      Instance ThreadParam As Long
      Instance running_ As Long
      Instance hStop_ As Dword
      Instance notifier_ As iFilesystemWatchNotifier
      Instance notifyRoutine_ As Dword
      Instance initialDirectory_ As WString
      Instance watchFor_ As Dword
      Instance watchTree_ As Long
     
      Thread Method Main() As Long
      #Align 4
        Local buffer As fswBuffer
        Local hover As OVERLAPPED
        Local hWait() As Dword
        Local dwWaitStatus, hDir As Dword
        Local fullPath, modFile As WString
        Local finished, numBytes, cnt, i As Long
        Local fn As WString Ptr
        Local msg As String
        Local fi As FILE_NOTIFY_INFORMATION
     
        running_ = -1
        ReDim hWait(1) As Local Dword
     
        If IsFalse IsFolder( initialDirectory_ ) Or notifyRoutine_=0 Or watchFor_=0 Then Exit Method
        Call StartFilesystemWatchNotifier( notifier_, initialDirectory_, notifyRoutine_ )
     
        fullPath = initialDirectory_
     
        hDir = CreateFile( ByVal StrPtr(fullPath), %FILE_LIST_DIRECTORY, %FILE_SHARE_READ Or %FILE_SHARE_WRITE Or %FILE_SHARE_DELETE, ByVal %NULL, %OPEN_EXISTING, %FILE_FLAG_BACKUP_SEMANTICS Or %FILE_FLAG_OVERLAPPED , %NULL)
     
        hWait(0) = CreateEvent( ByVal %NULL, 0, 0, ByVal %NULL ): ' File system change
        hWait(1) = CreateEvent( ByVal %NULL, 0, 0, ByVal %NULL ): ' Stop
     
        hover.hEvent  = hWait(0)
        hStop_        = hWait(1)
     
        Do While -1
          Memory Fill VarPtr( buffer ), %BUFSIZE, Byte 0
          ResetEvent( hover.hEvent )
          If ReadDirectoryChangesW( hDir, VarPtr(buffer), %BUFSIZE, watchTree_, watchFor_, %NULL, hover, %NULL) Then
            dwWaitStatus = WaitForMultipleObjects( 2, hWait(0), %FALSE, %INFINITE )
            Select Case Const dwWaitStatus - %WAIT_OBJECT_0
              Case 0: ' Directory change signaled
                  msg = buffer
                  notifier_.Changed( msg )
              Case 1: ' hStop signaled
                Exit Loop
            End Select
          End If
        Loop
        hStop_ = 0
        CloseHandle( hWait(0) )
        CloseHandle( hWait(1) )
        CloseHandle( hDir )
        notifier_.Stop()
        notifier_ = Nothing
        running_ = 0
      End Method
     
      Interface iFilesystemWatcher $FSW_IID
        Inherit IPowerThread
     
        Method Initialize Alias "Initialize"( initialDirectory As WString, notifyRoutine As Dword, watchFor As Dword, ByVal watchTree As Long )
          If initialDirectory="" Or notifyRoutine=0 Or watchFor=0 Or watchTree=0 Then Exit Method
          If initialDirectory_ = "" Or notifyRoutine_ = 0 Or watchFor_=0 Or watchTree_ =0 Then
            initialDirectory_ = initialDirectory
            If Right$( initialDirectory_, 1 )<>"\" Then initialDirectory_ += "\"
            notifyRoutine_ = notifyRoutine
            watchFor_ = watchFor
            watchTree_ = IIf&( IsTrue( watchTree ), %TRUE, %FALSE )
          End If
        End Method
     
        Property Get WatchFor Alias "WatchFor" () As Dword
          Property = watchFor_
        End Property
     
        Property Get Running Alias "Running" () As Long
          Property = Me.IsAlive()
        End Property
     
        Property Get InitialDirectory Alias "InitialDirectory" () As WString
          Property = initialDirectory_
        End Property
     
        Property Get NotifyRoutine Alias "NotifyRoutine"() As Dword
          Property = notifyRoutine_
        End Property
     
        Method Stop Alias "Stop"()
          Local fw As iFilesystemWatcher
          fw = Me
          SetEvent( hStop_ )
          fw.Join( fw, 0 )
          fw = Nothing
        End Method
     
      End Interface
    End Class
     
    Sub StartFilesystemWatchNotifier( obj As iFilesystemWatchNotifier, initialDirectory As WString, ByVal notifyRoutine As Dword )
      obj = Class "cFilesystemWatchNotifier"
      obj.Initialize( initialDirectory, notifyRoutine )
      obj.Launch( ByVal 0 )
    End Sub
     
    Class cFilesystemWatchNotifier Common
      Instance ThreadParam As Long
      Instance hChange_ As Dword: ' Changed handle
      Instance hStop_ As Dword: ' Stop handle
      Instance stack_ As iStringStack: ' List of notifications sent
     
      Instance initialDirectory_ As WString
      Instance notifyRoutine_ As Dword
     
      Class Method Create()
        Call NewStringStack( stack_ )
      End Method
     
      Thread Method Main() As Long
        Local cnt As Long
        Local fi As FILE_NOTIFY_INFORMATION Ptr
        Local modFile As WString
        Local relativePath As String
        Local i, finished As Long
        Local hWait() As Dword
        Local ofs As Long
        Dim hWait(1) As Local Dword
        Local dwWaitStatus As Dword
        Local buffer As String
        Local filename As Word Ptr
     
        hWait(0) = CreateEvent( ByVal %NULL, 0, 0, ByVal %NULL ): ' Signal for change
        hWait(1) = CreateEvent( ByVal %NULL, 0, 0, ByVal %NULL ): ' Signal for time to stop
        hChange_ = hWait(0)
        hStop_   = hWait(1)
     
     
        Do While -1
          ResetEvent( hChange_ )
          dwWaitStatus = WaitForMultipleObjects( 2, hWait(0), %FALSE, %INFINITE )
          Select Case Const dwWaitStatus - %WAIT_OBJECT_0
            Case 0: ' Changed
              While FilesystemWatchNotifierPop( stack_, buffer )
                fi = StrPtr( buffer )
                finished = 0
                While Not finished
                  finished = ( @fi.NextEntryOffset = 0 )
                  If @fi.FileNameLength Then
                    modFile = Peek$$( VarPtr( @fi.FileName ), @fi.FileNameLength )
                    filename = StrPtr( modFile )
                    For i=0 To @fi.FileNameLength-1
                      If @filename[i] < 32 Then
                        modFile = Left$( modFile, i )
                        Exit For
                      End If
                    Next
                    modFile = initialDirectory_ + modFile
                    Call Dword notifyRoutine_ Using FilesystemWatcherNotifyPattern( modFile, @fi.Action )
                  End If
                  fi += @fi.NextEntryOffset
                Wend
              Wend
            Case 1: ' Stopped
              Exit Loop
          End Select
        Loop
      End Method
     
      Interface iFilesystemWatchNotifier
        Inherit IPowerThread
     
        Method Initialize( initialDirectory As WString, ByVal notifyRoutine As Dword )
          If notifyRoutine_ Then Exit Method
          notifyRoutine_ = notifyRoutine
          initialDirectory_ = initialDirectory
        End Method
     
        Method Stop()
          Local fwn As iFilesystemWatchNotifier
          SetEvent( hStop_ )
          fwn = Me
          fwn.Join( fwn, 0 )
          fwn = Nothing
        End Method
     
        Method Changed( msg As String )
          FilesystemWatchNotifierPush(  stack_, msg )
          SetEvent( hChange_ )
        End Method
      End Interface
    End Class
     
    Sub FilesystemWatchNotifierPush( stack As iStringStack, value As String ) ThreadSafe
      If IsObject( stack ) Then
        stack.Push( value )
      End If
    End Sub
     
    Function FilesystemWatchNotifierPop( stack As iStringStack, value As String ) ThreadSafe As Long
      If IsNothing( stack ) Or stack.Count=0 Then Exit Function
      value = stack.Pop
      Function = -1
    End Function
    StringStack.inc
    Code:
    Sub NewStringStack( obj As iStringStack )
      obj = Class "cStringStack"
    End Sub
     
    Class cStringStack
      Instance stack_ As IStackCollection
     
      Class Method Create()
        stack_ = Class "StackCollection"
      End Method
     
      Interface iStringStack
        Inherit IUnknown
     
        Property Get Count() As Long
          Property = stack_.Count
        End Property
     
        Method Push( value As String )
          stack_.Push( value )
        End Method
     
        Method Pop() As String
          Local v As Variant
     
          If stack_.Count Then
            v = stack_.Pop()
            Method = Variant$( v )
          End If
        End Method
      End Interface
    End Class
    Last edited by Larry Charlton; 26 Jul 2012, 08:21 AM.
    LarryC
    Website
    Sometimes life's a dream, sometimes it's a scream

  • #2
    FileSystemWatch Console Compiler with Includes

    Here's a demo using the Console compiler and including the source.

    TestFswCC.bas
    Code:
    #Compile Exe
    #Compiler PBCC 6
    #Dim All
     
    #Include "FilesystemWatcher.inc"
     
    Function PBMain () As Long
      Local fsw As iFilesystemWatcher
      Local a As String
     
      If IsFalse IsFolder( "c:\Test" ) Then
        MkDir "c:\test"
      End If
     
      Con.Print "Press enter to exit"
     
      Call StartFilesystemWatcher( fsw, "c:\test\", CodePtr( OnChange ), %fsWatchFor.All, 1 )
     
      Con.Line.Input a
     
      fsw.Stop()
    End Function
     
    Sub OnChange( filename As WString, ByVal changeEvent As Long )
      Con.Print filename; " ";
      Select Case Const changeEvent
        Case %fsEvent.Added:     Con.Print "Added"
        Case %fsEvent.Removed:   Con.Print "Removed"
        Case %fsEvent.Modified:  Con.Print "Modified"
        Case %fsEvent.OldName:   Con.Print "Rename Old name"
        Case %fsEvent.NewName:   Con.Print "Rename New name"
        Case Else:               Con.Print "Unknown &H" + Hex$( changeEvent )
      End Select
      filename = filename
    End Sub
    LarryC
    Website
    Sometimes life's a dream, sometimes it's a scream

    Comment


    • #3
      FileSystemWatcher Windows using includes

      Here's a demo for PBWin 10 using includes.

      TestFswWin.bas
      Code:
      #Compile Exe
      #Compiler PBWin 10
      #Dim All
       
      %UNICODE = 1
      #Include Once "win32api.inc"
      #Include Once "FilesystemWatcher.inc"
       
      Global fw As iFilesystemWatcher:  ' File watcher
      Global folder As WString: ' File we're watching
      Global hDlg As Dword:       ' Open dialog
       
      %FILE_EXIT    = 1005
      %FILE_OPEN    = 1006
      %CTL_TEXT     = 1001
       
      Function PBMain()
        Local hFont, hTopMenu, hFileMenu, hAccel, hRes As Dword
        Dim menuAccel(0) As ACCELAPI
       
        Dialog New Pixels, %HWND_Desktop, "File Watcher", 105, 114, 607, 483, %WS_OverlappedWindow, To hDlg
        Control Add TextBox, hDlg, %CTL_TEXT, "", 0, 0, 0, 0, %WS_Child Or _
                                                                  %WS_Visible Or _
                                                                  %WS_Border Or _
                                                                  %WS_TabStop Or _
                                                                  %WS_VScroll Or _
                                                                  %WS_HScroll Or _
                                                                  %ES_Left Or _
                                                                  %ES_MultiLine Or _
                                                                  %ES_ReadOnly Or _
                                                                  %ES_AutoHScroll Or _
                                                                  %ES_AutoVScroll,_
                                                                  %WS_Ex_RightScrollbar
        Font New "Courier New", 10, 0, %ANSI_CHARSET To hFont
        Control Set Font hDlg, %CTL_TEXT, hFont
       
        Menu New Bar To hTopMenu
        Menu New PopUp To hFileMenu
       
        Menu Add PopUp, hTopMenu, "File", hFileMenu, %MF_Enabled
        Menu Add String, hFileMenu, "&Open" & $Tab & "Ctrl+O", %FILE_OPEN, %MF_Enabled
        Menu Add String, hFileMenu, "-", 0, 0
        Menu Add String, hFileMenu, "E&xit", %FILE_EXIT, %MF_Enabled
       
        Menu Attach hTopMenu, hDlg
       
        menuAccel(0).fVirt = %FVIRTKEY Or %FCONTROL Or %FNOINVERT
        menuAccel(0).Key = Asc("O")
        menuAccel(0).Cmd = %FILE_OPEN
       
        Accel Attach hDlg, menuAccel() To hAccel
       
        Dialog Show Modeless hDlg, Call ShowFilesystemWatcherProc To hRes
        GetNewFile()
        If IsNothing( fw ) Then Exit Function
       
        Do
          Dialog DoEvents
        Loop While IsWin(hDlg)
       
        If IsObject( fw ) Then fw.Stop()
       
        Font End hFont
      End Function
       
      CallBack Function ShowFilesystemWatcherProc()
        Local w, h As Long
       
        Select Case Const Cb.Msg
          Case %WM_Size
            Dialog Get Client hDlg To w, h
            Control Set Size hDlg, %CTL_TEXT, w, h
       
          Case %WM_Command
            Select Case Const Cb.Ctl
              Case %FILE_OPEN: Call GetNewFile()
              Case %FILE_EXIT: Dialog End hDlg
            End Select
        End Select
      End Function
       
      Sub GetNewFile()
        Local newFolder As WString
       
        Display Browse 0, , , "Select Directory To Watch", "", %BIF_NewDialogStyle To newFolder
        If newFolder = "" Then Exit Sub
        If IsObject( fw ) Then fw.Stop()
        fw = Nothing
        folder = newFolder
        Call StartFilesystemWatcher( fw, newFolder, CodePtr( OnFileChanged ), %fsWatchFor.All, 1 )
      End Sub
       
      Sub OnFileChanged( filename As WString, ByVal changeEvent As Long )
        Static sb As IStringBuilderW
       
        If IsNothing( sb ) Then sb = Class "StringBuilderW"
       
        sb.Add( filename )
        sb.Add( " " )
       
        Select Case Const changeEvent
          Case %fsEvent.Added:     sb.Add( "Added" )
          Case %fsEvent.Removed:   sb.Add( "Removed" )
          Case %fsEvent.Modified:  sb.Add( "Modified" )
          Case %fsEvent.OldName:   sb.Add( "Old name" )
          Case %fsEvent.NewName:   sb.Add( "New name" )
          Case Else:               sb.Add( "Unknown &H" + Hex$( changeEvent ) )
        End Select
        sb.Add( $CrLf )
        Control Set Text hDlg, %CTL_TEXT, sb.String
      End Sub
      LarryC
      Website
      Sometimes life's a dream, sometimes it's a scream

      Comment


      • #4
        FileSystemWatcher SLL for CC

        Here's a demo of using it as an SLL.



        To build:
        1. compile FswSll.bas, creates FswSll.sll
        2. compile and run TestFswSllCC.bas
        To see the SLL with PBWin 10 use the PBWin demo above and replace the #Include "FilesystemWatcher.inc" with the two lines:
        Code:
        #Include "FswSll.inc"
        #Link "FswSll.sll"
        FswSll.bas
        Code:
        #Compile SLL
        #Compiler PBWin 10, PBCC 6
        #Dim All
         
        %UNICODE = 1
        #Include Once "win32api.inc"
        #Include Once "FilesystemWatcher.inc"
        FswSll.inc
        Code:
        ' Include file for FswSll.sll
         
        ' File system changes to watch for
        ' Can be any combination of the following
        Enum fsWatchFor
          Filename      = &H1
          DirectoryName = &H2
          Attributes    = &H4
          Size          = &H8
          Write         = &H10
          Access        = &H20
          Creation      = &H40
          Security      = &H100
          All           = &H17f
        End Enum
         
        ' List of change events sent to notification
        Enum fsEvent
          Added = 1
          Removed = 2
          Modified = 3
          OldName = 4
          NewName = 5
        End Enum
        TestFswSllCC.bas
        Code:
        #Compile Exe
        #Compiler PBCC 6
        #Dim All
         
         %UNICODE = 1
        #Include "win32api.inc"
        
        #Include "FswSll.inc"
        #Link "FswSll.sll"
         
        Function PBMain () As Long
          Local fsw As iFilesystemWatcher
          Local a As WString
         
          If IsFalse IsFolder( "c:\Test" ) Then
            MkDir "c:\test"
          End If
         
          Con.Print "Press enter to exit"
         
          Call StartFilesystemWatcher( fsw, "c:\test\", CodePtr( OnChange ), %fsWatchFor.All, 1 )
         
          Con.Line.Input a
         
          fsw.Stop()
        End Function
         
        Sub OnChange( filename As WString, ByVal changeEvent As Long )
          Con.Print filename; " ";
          Select Case Const changeEvent
            Case %fsEvent.Added:     Con.Print "Added"
            Case %fsEvent.Removed:   Con.Print "Removed"
            Case %fsEvent.Modified:  Con.Print "Modified"
            Case %fsEvent.OldName:   Con.Print "Rename Old name"
            Case %fsEvent.NewName:   Con.Print "Rename New name"
            Case Else:               Con.Print "Unknown &H" + Hex$( changeEvent )
          End Select
          filename = filename
        End Sub
        Last edited by Larry Charlton; 26 Jul 2012, 08:50 AM.
        LarryC
        Website
        Sometimes life's a dream, sometimes it's a scream

        Comment


        • #5
          FileSystemWatcher DLL with CC

          Demo of using the FileSystemWatcher from a DLL using the CC.

          To build:
          1. Compile FswDll.bas to create FswDll.dll
          2. Compile and run TestFswDllCC.bas
          To see the demo of using a DLL With PBWin 10, in the windows demo above, replace #Include "FilesystemWatcher.inc" with the following line:
          Code:
          #Include "FswDll.inc"

          FswDll.bas
          Code:
          #Compile DLL
          #Compiler PBWin 10
          #Dim All
           
          %UNICODE = 1
          #Include Once "win32api.inc"
          #Include Once "FilesystemWatcher.inc"
          FswDll.inc
          Code:
          ' Include file for FswDll.dll
           
          ' File system changes to watch for
          ' Can be any combination of the following
          Enum fsWatchFor
            Filename      = &H1
            DirectoryName = &H2
            Attributes    = &H4
            Size          = &H8
            Write         = &H10
            Access        = &H20
            Creation      = &H40
            Security      = &H100
            All           = &H17f
          End Enum
           
          ' List of change events sent to notification
          Enum fsEvent
            Added = 1
            Removed = 2
            Modified = 3
            OldName = 4
            NewName = 5
          End Enum
           
          Declare Sub StartFilesystemWatcher Lib "FswDll.dll" Alias "StartFilesystemWatcher"( obj As iFilesystemWatcher, initialDirectory As WString, ByVal notifyRoutine As Dword, ByVal watchFor As Dword, ByVal watchTree As Long )
           
          $FSW_IID = Guid$("{B91C1ECC-39E5-4415-AE17-CC9868470758}")
           
          Interface iFilesystemWatcher $FSW_IID
            Inherit IPowerThread
           
            Method Initialize( initialDirectory As WString, notifyRoutine As Dword, watchFor As Dword, ByVal watchTree As Long )
            Property Get WatchFor() As Dword
            Property Get Running() As Long
            Property Get InitialDirectory() As WString
            Property Get NotifyRoutine() As Dword
            Method Stop()
          End Interface
          TestFswDllCC.bas
          Code:
          #Compile Exe
          #Compiler PBCC 6
          #Dim All
           
          #Include "FswDll.inc"
           
          Function PBMain () As Long
            Local fsw As iFilesystemWatcher
            Local a As String
           
            If IsFalse IsFolder( "c:\Test" ) Then
              MkDir "c:\test"
            End If
           
            Con.Print "Press enter to exit"
           
            Call StartFilesystemWatcher( fsw, "c:\test\", CodePtr( OnChange ), %fsWatchFor.All, 1 )
           
            Con.Line.Input a
           
            fsw.Stop()
          End Function
           
          Sub OnChange( filename As WString, ByVal changeEvent As Long )
            Con.Print filename; " ";
            Select Case Const changeEvent
              Case %fsEvent.Added:     Con.Print "Added"
              Case %fsEvent.Removed:   Con.Print "Removed"
              Case %fsEvent.Modified:  Con.Print "Modified"
              Case %fsEvent.OldName:   Con.Print "Rename Old name"
              Case %fsEvent.NewName:   Con.Print "Rename New name"
              Case Else:               Con.Print "Unknown &H" + Hex$( changeEvent )
            End Select
            filename = filename
          End Sub
          LarryC
          Website
          Sometimes life's a dream, sometimes it's a scream

          Comment


          • #6
            Updated Source

            I updated the source here. The DLL is included. Source should compile with either the PB includes or José's. All demos for PB/CC and PB/WIN are included.

            There's an issue with using SLL's with this code. The Console demo works, the Windows demo does not. I would not recommend using the SLL at this time. Shouldn't matter since you can use either set of includes now.
            LarryC
            Website
            Sometimes life's a dream, sometimes it's a scream

            Comment

            Working...
            X