Search code examples
powershellfilesystemwatcher

Powershell FileSystemWatcher stops notifications after some time


I have a simple powerscript which watch for a folder for new file addition. The script is watching a root folder. This root folder has multiple sub folders. User can copy a file to any of these sub folders and script will send notification to the user saying a file has been arrived.

The question here is - when the script is started from command line argument, it works for some time. It sends notifications when there is a new file copied to any folder within the root folder. But email notifications are not consistent. It automatically stops sending notifications after couple of minutes even if there is a new file dropped at the folder.

I am using version 5 of Powershell.exe

There is no error in the script. Script still shows status as running but notifications stops.

FileSytemWatcher is working for me but it's not reliable. Experts any suggestions please.

PS: There is no error printed from Catch block

$FileSystemWatcher = new-object system.io.FileSystemWatcher
$FileSystemWatcher.path="\\networklocation\folder"
$FileSystemWatcher.Includesubdirectoriesncludesubdirectories=$true
$FileSystemWatcher.EnableRaisingEventsnableraisingevents=$true

$action={
   try
    {
        $detail=$event.SourceEventArgs
        $FullPath=$details.FullPath
        $ChangeType=$details.ChangeType
        $FileName=$event.SourceEventArgs.Name
        $EmailBody="Something has arrived"
        
        switch($ChangeType)
        {
            'Created' { "CREATED"
                        SendEmail $fromEmailID $usereEmailID $cc $Subject $EMailBody
                        Start-Sleep -Seconds 1
                      }
        }
    }
    catch
    {
        Logwrite($_)
        Write-Host "An error has occured"
        Write-Host $_
    }
    }
    
$handlers = .{
                Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Created -Action $Action -SourceIdentifier FSCreate
                }

try
{
    do
        {
            Wait-Event -Timeout 1
        }while ($true)
            
}       
catch
    {
        Logwrite($_)
    }
finally
    {
        LogWrite('Finally')
        Unregister-Event -SourceIdentifier FSCreate
        $handers |
        Remove-Job
        $FileSystemWatcher.EnableRaisingEvents=$false
        $FileSystemWatcher.Dispose()
    }
    
    

Solution

  • Okay, so much like a couple of the comments stated, there were a few typos in your code... Those were making this impossible to run. I've also cleaned up the syntax a bit. I'm not sure if those were just issues you encountered when pasting your code here or what... I've included the cleaned up code below (NOTE: I did change the order of the parameters passed to SendEmail due to my own iteration of the function)

    Either way, after writing a couple of custom functions to replace LogWrite and SendEmail, the code seems to work fine. A few questions and suggestions to get to the root of your problem:

    1. What OS are you running on? There are certain limitations that are imposed by different iterations of Windows.
    2. How are you running this? (I.E. Scheduled Task, From the PowerShell terminal, ISE, etc...)
    3. Is there any type of network limitation in place which is seeing your outgoing mail as some sort of threat and blocking it? Perhaps have the SendMail function log to a file as well.
    4. There are some limitations to the FSW from what I understand, including the InternalBufferSize which can overflow, causing issues. According to Microsoft (See references below) "The maximum size you can set for the InternalBufferSize property for monitoring a directory over the network is 64 KB." Perhaps resetting the watchers every X number of events using a built in iterator would be a fair compromise to curb this issue.

    Code

    $FileSystemWatcher = New-Object System.IO.FileSystemWatcher
    $FileSystemWatcher.Path = "\\networklocation\folder"
    $FileSystemWatcher.IncludeSubDirectories = $true
    $FileSystemWatcher.EnableRaisingEvents = $true
    
    $action = {
        try {
            $detail = $event.SourceEventArgs
            $FullPath = $detail.FullPath
            $ChangeType = $detail.ChangeType
            $FileName = $event.SourceEventArgs.Name
            $EmailBody = "$FileName has arrived"
            $Subject = 'A File Has Arrived!'
            
            $Message = "$FileName has arrived"
            LogWrite($Message)
            
            switch ($ChangeType) {
                'Created' {
                    "CREATED"
                    SendEmail $userEmailID $Subject $EMailBody $fromEmailID #$cc
                    Start-Sleep -Seconds 1
                }
            }
        } catch {
            Logwrite($_)
            Write-Host "An error has occured"
            Write-Host $_
        }
    }
        
    $handlers = . {
        Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Created -Action $Action -SourceIdentifier FSCreate
    }
    
    try {
        do {
            Wait-Event -Timeout 1
        }while ($true)
    } catch {
        Logwrite($_)
    } finally {
        LogWrite('Enter Finally')
        Unregister-Event -SourceIdentifier FSCreate
        $handers | Remove-Job
        $FileSystemWatcher.EnableRaisingEvents = $false
        $FileSystemWatcher.Dispose()
    }
    

    Edit: RE: user1386121 7/10/20

    The following code is an example of resetting the File System Watcher every X number of events using a built in iterator.

    function Initialize-FileSystemWatcher {
        $FileSystemWatcher = New-Object System.IO.FileSystemWatcher
        $FileSystemWatcher.Path = "\\networklocation\folder"
        $FileSystemWatcher.IncludeSubDirectories = $true
        $FileSystemWatcher.EnableRaisingEvents = $true
    
        $action = {
            try {
                $detail = $event.SourceEventArgs
                $FullPath = $detail.FullPath
                $ChangeType = $detail.ChangeType
                $FileName = $event.SourceEventArgs.Name
                $EmailBody = "$FileName has arrived"
                $Subject = 'A File Has Arrived!'
            
                $Message = "$FileName has arrived"
                LogWrite($Message)
            
                switch ($ChangeType) {
                    'Created' {
                        "CREATED"
                        SendEmail $userEmailID $Subject $EMailBody $fromEmailID #$cc
                        Start-Sleep -Seconds 1
                    }
                }
            } catch {
                Logwrite($_)
                Write-Host "An error has occured"
                Write-Host $_
            }
        }
        
        $handlers = . {
            Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Created -Action $Action -SourceIdentifier FSCreate
        }
        @{
            Watcher = $FileSystemWatcher
            Handler = $handlers
        }
    }
    
    try {
        LogWrite('Enter Try...')
        $MaxEvents = 5
        while ($true) {
            Write-Host 'New Watcher!'
            $IFSW = Initialize-FileSystemWatcher
            while ($IFSW.Handler.Output.Count -le $MaxEvents) {
                Wait-Event -Timeout 1
            }
            LogWrite('Cleaning Up FileSystemWatcher')
            Unregister-Event -SourceIdentifier FSCreate
            $IFSW.Watcher.EnableRaisingEvents = $false
            $IFSW.Watcher.Dispose()
            $IFSW.Handler.Dispose()
            $IFSW = $null
        }
    } catch {
        Logwrite($_)
    } 
    

    References