FileSystemWatcher cmdlet in Powershell not monitoring system32 folder and subfolders - powershell

I noticed that when using PowerShell's FileSystemWatcher cmdlet that it doesnt seem to monitor System32 or it's subfolders in my Windows 7 computer. A script Í have that works just fine when monitoring subfolders of My Documents (i/e "C:\Users\W\Documents\Fichiers PowerShell" is a fplder path that works) but doesn't work when I substitute a folder path in System32 (i/e C:\Windows\System32\Recovery is a path that doesn't work)
Here is the script i'm working with but System32 paths haven't worked in other FileSystemWatcher scripts. Any advice as to a workaround would be appreciated . I must monitor C:\Windows\System32\Recovery. Thank You.
function FileSystemWatcher([string]$log = "C:\Users\W\Documents\Fichiers PowerShell\newfiles.txt",
[string]$folder = "C:\Windows\System32\Recovery",
[string]$filter = "*ps1",
[char]$timeout = 1000
){
$FileSystemWatcher = New-object System.IO.FileSystemWatcher $folder, $filter
Write-Host "Press any key to abort monitoring $folder"
do {
$result = $FileSystemWatcher.WaitForChanged("created", $timeout)
if ($result.TimedOut -eq $false) {
$result.Name |
Out-File $log -Append
Write-Warning ("Detected new file: " + $result.name)
$filename = (gc $log -ea 0)
ii "C:\Program Files (x86)\Mozilla Firefox\firefox.exe"
remove-item "C:\Users\W\Documents\Fichiers PowerShell\newfiles.txt"
}
} until ( [System.Console]::KeyAvailable )
Write-Host "Monitoring aborted."
Invoke-Item $log}
FileSystemWatcher

Give this a try:
$fsw = New-Object System.IO.FileSystemWatcher C:\Windows\System32 -Property #{
IncludeSubdirectories = $true
NotifyFilter = [System.IO.NotifyFilters]::DirectoryName
}
$event = Register-ObjectEvent $fsw Created -SourceIdentifier DirectoryCreated -Action {
Write-Host "$($event.SourceArgs | fl * | Out-String)"
}
md C:\Windows\System32\temp2

Related

Filesystemwatcher file created event not decompressing files

I got a simple script that should watch for new files in specified folder and uncompress them on arriving. Simple, but my script only works under the debugger of Powershell ISE. When outside the debugger, on file creation it only prints "New file xxx found" and "Done", without decompressing it, nor deleting, but also no errors. I'm I missing something here?
# Folder you to monitor
$PathToMonitor = "C:\tmp1".
$OutputPath = "C:\tmp"
$Filter = "*.zip" # Filter for zip files.
$FileSystemWatcher = New-Object System.IO.FileSystemWatcher
$FileSystemWatcher.Path = $PathToMonitor
$FileSystemWatcher.IncludeSubdirectories = $false
$FileSystemWatcher.Filter = $Filter
# make sure the watcher emits events
$FileSystemWatcher.EnableRaisingEvents = $true
# define the code that should execute when a file change is detected
$Action = {
$details = $event.SourceEventArgs
$FullPath = $details.FullPath
$OldName = $details.OldName
$Timestamp = $event.TimeGenerated
try
{
Write-Host "New file " + $FullPath + " found"
$TarProgram = "tar.exe"
$TarArgs = 'xvf "' + $FullPath + '" -C ' + $OutputPath
Start-Process $TarProgram $TarArgs -Wait -WindowStyle hidden
Remove-Item -Path $FullPath -Force
Write-Host "Done"
}
catch [System.SystemException]
{
Write-Host $_.ScriptStackTrace
}
}
# add event handlers
$handlers = . {
Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Created -Action $Action -SourceIdentifier FSCreateFtpGe
}
Write-Host "Watching for changes to $PathToMonitor"
try
{
do
{
Wait-Event -Timeout 10
} while ($true)
}
catch [System.SystemException]
{
Write-Host $_.ScriptStackTrace
}
finally
{
# this gets executed when user presses CTRL+C
# remove the event handlers
Write-Host "Cleaning up"
Unregister-Event -SourceIdentifier FSCreateFtpGe
# remove background jobs
$handlers | Remove-Job
# remove filesystemwatcher
$FileSystemWatcher.EnableRaisingEvents = $false
$FileSystemWatcher.Dispose()
"Event Handler disabled."
}
Why do you have a period here...
$PathToMonitor = "C:\tmp1".
...that is invalid syntax and the script should fail anyway. Yet, that could be just a type you did on posting here.
Why are you using tar vs the PowerShell built-in archive cmdlets?
if tar.exe is not in your PowerShell or system paths, you have to fully qualify that path to it for it to be used.
$TarProgram = 'C:\ProgramFiles\tar.exe'
So, your code should work in any PowerShell host (ISE [in/out of debug mode])/powershell.exe/VSCode, et al. AS I just tested it. Yet again, the cmdlets are there. Example of the below running in the new Windows Terminal.
#region Begin Script
# Folder you to monitor
$PathToMonitor = 'D:\tmp1'
$OutputPath = 'D:\tmp'
$Filter = '*.zip' # Filter for zip files.
$FileSystemWatcher = New-Object System.IO.FileSystemWatcher
$FileSystemWatcher.Path = $PathToMonitor
$FileSystemWatcher.IncludeSubdirectories = $false
$FileSystemWatcher.Filter = $Filter
# make sure the watcher emits events
$FileSystemWatcher.EnableRaisingEvents = $true
# define the code that should execute when a file change is detected
$Action = {
$details = $event.SourceEventArgs
$FullPath = $details.FullPath
$OldName = $details.OldName
$Timestamp = $event.TimeGenerated
try
{
"New file $FullPath found"
Expand-Archive -Path $FullPath -DestinationPath 'D:\tmp'
# Start-Process $TarProgram $TarArgs -Wait -WindowStyle hidden
Remove-Item -Path $FullPath -Force
'Done'
}
catch [System.SystemException]
{$PSItem.ScriptStackTrace}
}
# add event handlers
$handlers = . {
$RegisterObjectEventSplat = #{
InputObject = $FileSystemWatcher
EventName = 'Created'
Action = $Action
SourceIdentifier = 'FSCreateFtpGe'
}
Register-ObjectEvent #RegisterObjectEventSplat
}
"Watching for changes to $PathToMonitor"
try
{
do { Wait-Event -Timeout 10 }
while ($true)
}
catch [System.SystemException]
{$_.ScriptStackTrace}
finally
{
<#
this gets executed when user presses CTRL+C
remove the event handlers
#>
'Cleaning up'
Unregister-Event -SourceIdentifier FSCreateFtpGe
# remove background jobs
$handlers |
Remove-Job
# remove filesystemwatcher
$FileSystemWatcher.EnableRaisingEvents = $false
$FileSystemWatcher.Dispose()
'Event Handler disabled.'
}
#endregion End Script
# Results
Get-CimInstance -ClassName Win32_OperatingSystem | Select-Object -Property Caption, Version
Caption Version
$PSVersionTable
Name Value
---- -----
PSVersion 5.1.18362.752
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0, 5.0, 5.1.18362.752}
BuildVersion 10.0.18362.752
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
Get-ChildItem -Path 'D:\tmp' -Verbose
(Get-ChildItem -Path 'D:\tmp').Count
0
Get-ChildItem -Path 'D:\tmp1' -Verbose
(Get-ChildItem -Path 'D:\tmp1').Count
0
.\FSWCopyUnzip.ps1
Watching for changes to D:\tmp1
New file D:\tmp1\book1.zip found
Done
Cleaning up
Get-ChildItem -Path 'D:\tmp' -Verbose
Directory: D:\tmp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 13-Mar-20 12:19 70 abc.txt
-a---- 13-Mar-20 22:27 71 book1.txt
-a---- 06-Apr-20 21:51 2979 Data.zip
-a---- 04-Mar-20 01:04 72 FileWithLines.txt
Get-ChildItem -Path 'D:\tmp1' -Verbose
(Get-ChildItem -Path 'D:\tmp1').Count
0
Just for the records, I found the issue. It's somehow related to variables scopes in Powershell.
If instead of using a variable declared at the begin of the script to hold the value of $OutputPath I use a literal string then it works. Otherwise, it complains that the value is null at runtime (In Powershell ISE it shows a value)
So, the action block now looks like this:
$Action = {
$details = $event.SourceEventArgs
$FullPath = $details.FullPath
$OldName = $details.OldName
$Timestamp = $event.TimeGenerated
try
{
Expand-Archive -LiteralPath $FullPath -DestinationPath "C:\Temp"
Remove-Item -LiteralPath "$FullPath" -Force
}
catch [System.SystemException]
{
Write-Host $_
}
}
I would prefer to have all my vars at the begin, so they can be easily changed if needed, but at least this version works.

Powershell Scipt Runs in ISE only

I have researched this question quite a bit and have found others with similar issues, but no fix that works for us.
We have built a power shell script to monitor a folder for new images, check their color profile with exiftool and convert when needed with image magick. All of this functionality works well enough when the script is run in ISE, but will not work if the script is executed from a .exe or ran manually.
I have read that FileSystemWatcher requires the script be ran in STA which I have attempted to do a few times by including -STA in the target for the .exe shortcut. Doing so proves unsuccessful.
#----------------------------------------------------------------------------
Unregister-Event -SourceIdentifier FileCreated
Clear
#----------------------------------------------------------------------------
$description = Get-WmiObject -Class Win32_OperatingSystem |Select Description
$description = $description -Replace "#{Description=",""
$description = $description -Replace "}",""
$assetsFolder = "E:\" + $description + "\Capture One Session\Output"
$conversionStage = "C:\Profile_Pro\conversionStage"
#----------------------------------------------------------------------------
New-Item -ItemType directory -Force -Path "$assetsFolder" | Out-Null
New-Item -ItemType directory -Force -Path "$assetsFolder" | Out-Null
#----------------------------------------------------------------------------
$filter = "*.*"
$srgb = "sRGB IEC61966-2.1"
$isTif = "tif"
#----------------------------------------------------------------------------
$monitor = New-Object IO.FileSystemWatcher $assetsFolder, $filter -Property #{
IncludeSubdirectories = $true
NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'
}
#----------------------------------------------------------------------------
$onCreated = Register-ObjectEvent $monitor Created -SourceIdentifier FileCreated -Action {
#----------------------------------------------------------------------------
Add-Type -AssemblyName System.Windows.Forms
#----------------------------------------------------------------------------
$path = $event.SourceEventArgs.FullPath
$name = $event.SourceEventArgs.Name
Write-Host $path
Write-Host $name
If ($name -Match $isTif)
{
$BOX = [System.Windows.Forms.MessageBox]::Show('TIFF FILE DETECTED. PLEASE CHECK RECIPE.', 'Warning', 'ok', 'Warning')
$path = $path -Replace ".tif.tmp",".tif"
Start-Sleep -s 2
Remove-Item "$path" -Force
}
Else
{
$colorSpace = C:\Profile_Pro\tools\exiftool.exe -T -ProfileDescription $path
$profileConvert = 'C:\Profile_Pro\tools\sRGB_Color_Space_Profile.icm'
If ($colorSpace -Match $srgb)
{
}
Else
{
Start-Sleep -s 3
$name = $name -Replace ".jpg.tmp",".jpg"
$path = $path -Replace ".jpg.tmp",".jpg"
Move-Item -Path $path -Destination $conversionStage -Force
convert $conversionStage\$name -profile $profileConvert $path
Start-Sleep -s 1
Remove-Item "$conversionStage\$name" -Force
#----------------------------------------------------------------------------
$BOX = [System.Windows.Forms.MessageBox]::Show('COLOR PROFILE ERROR. PLEASE CHECK RECIPE.', 'Warning', 'ok', 'Warning')
#----------------------------------------------------------------------------
$TO = $env:USERNAME + "#biz.com"
$Outlook = New-Object -ComObject Outlook.Application
$Mail = $Outlook.CreateItem(0)
$Mail.To = $TO
$Mail.Subject = "COLOR PROFILE DEFECT: " + $name
$Mail.importance = 2
$Mail.Body = "A COLOR PROFILE DEFECT HAS BEEN DETECTED WITH FILE:`r`n`r`n$name`r`n`r`nTHIS FILE IS FIXED.`r`n`r`nPLEASE CHECK YOUR PROCESS RECIPE."
$Mail.Send()
}
}
}
#----------------------------------------------------------------------------
#Unregister-Event -SourceIdentifier FileCreated
#----------------------------------------------------------------------------
Any ideas to keep this script running as a .exe or outside of ISE would be greatly appreciated.

FileSystemWatcher detect when file is moved to folder

I'm having trouble with a script that's monitoring a folder.
FileSystemWatcher only seems to detect when a file is being copied to the folder, not when it's just being moved to it from the same drive.
Is there a way to detect this?
$desFolder = "H:\Media"
$ExFolder = "H:\Monitor"
$Filter = '*.*'
$fsw = New-Object IO.FileSystemWatcher $ExFolder, $Filter
$fsw.IncludeSubdirectories = $true
$fswOnChange = Register-ObjectEvent -InputObject $fsw -EventName Changed -SourceIdentifier FileUpdated -Action {
$File = Get-Item $EventArgs.FullPath
if($File.Name -imatch '\.(?:mp4|mkv)$' -and $file.Name -match '.*?S\d+E+'){
Start-Sleep -s 1
if(Test-FileReady $File.FullName){
Move-Files $File
}
}
}
function global:Test-FileReady {
Param([parameter(Mandatory=$true)][string]$Path)
if (Test-Path -LiteralPath $Path) {
trap {
return $false
}
$stream = New-Object system.IO.StreamReader $Path
if ($stream) {
$stream.Close()
return $true
}
}
}
function global:Move-Files{
Param([parameter(Mandatory=$true)][System.IO.FileInfo]$File)
Write-Host $File.Name
}
Try using Renamed and Created events as well.
BTW, the IO.FileSystemWatcher docs say:
The component will not watch the specified directory until the Path is set, and EnableRaisingEvents is true
$fsw.EnableRaisingEvents = $true

Use of Start-job for WatcherChangeTypes

Is there a way to monitor the file changes in the directory in the powershell background.
I was trying to do as below.
Start-Job {
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = get-location
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $false
$watcher.NotifyFilter = [System.IO.NotifyFilters]::LastWrite -bor [System.IO.NotifyFilters]::FileName
while($true){
$result = $watcher.WaitForChanged([System.IO.WatcherChangeTypes]::Changed -bor [System.IO.WatcherChangeTypes]::Renamed -bOr [System.IO.WatcherChangeTypes]::Created, 1000);
if($result.TimedOut){
continue;
}
Add-Content D:\receiver.txt "file name is $($result.Name)"
}
}
This does not work as expected. I do not get any information on the receiver.txt file. Although the script works as expected if I dont use start-job.
Start-Job will launch your job in a new context, so the working directory will be the default directory (e.g. \Users\Username) and Get-Location will return this directory.
One way to deal with this is to save the original working directory, pass it to the job as an argument, and set the working directory in the job using Set-Location.
$currentLocation = Get-Location
Start-Job -ArgumentList $currentLocation {
Set-Location $args[0];
...
}
I would use Register-ObjectEvent and track each event type. It uses the same PSJobs that you would use in Start-Job, but geared towards monitoring the actual event and running a specific action based on what you give it.
Not tested:
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = get-location
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $false
$watcher.NotifyFilter = [System.IO.NotifyFilters]::LastWrite -bor [System.IO.NotifyFilters]::FileName
ForEach ($Item in #('Changed','Renamed','Created')) {
(Register-ObjectEvent -EventName $Item -InputObject $watcher -Action {
#Set up a named mutex so there are no errors accessing an opened file from another process
$mtx = New-Object System.Threading.Mutex($false, "FileWatcher")
$mtx.WaitOne()
Add-Content D:\receiver.txt "file name is $($result.Name)"
#Release so other processes can write to file
[void]$mtx.ReleaseMutex()
})
}
Quick way to stop the FileSystemWatcher
Get-EventSubscriber | Unregister-Event
Get-Job | Remove-Job -Force

Check for file creation in a loop

Can I run this script one time automate when the .exe show up on folder?
Get-ChildItem -Filter *.exe | ForEach {
&$_.Fullname /s
I want a script to check if .exe file shows up on folder, then run that commands, else check again for .exe file.
You can use the FileSystemWatcher to listen for these events.
Get-EventSubscriber -SourceIdentifier ExeCreated -ErrorAction SilentlyContinue | Unregister-Event;
$Watcher = New-Object -TypeName System.IO.FileSystemWatcher;
$Watcher.Filter = '*.exe';
$Watcher.Path = 'c:\test';
$Action = { Write-Host -Object ('New file created: {0}' -f $event.SourceArgs[1].Name); };
Register-ObjectEvent -InputObject $Watcher -EventName Created -Action $Action -SourceIdentifier ExeCreated;