I need to monitor a folder and perform some action each time a file is created. I have 2 solutions - one using WMI, where I can use this filter (called from either a .MOF
file or a Powershell script which registers permanent MWI event bindings) to poll the folder every second :
SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA "Cim_DirectoryContainsFile" AND TargetInstance.GroupComponent="Win32_Directory.Name='C:\\test'"
Example Script :
$query = @"
SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA "Cim_DirectoryContainsFile" AND TargetInstance.GroupComponent="Win32_Directory.Name='C:\\test'"
"@
#Set up hash table for splatting
$wmiParams = @{
Computername = $env:COMPUTERNAME
ErrorAction = 'Stop'
NameSpace = 'root\subscription'
}
######################################################################################################################### Filter
#Creating a new event filter
$wmiParams.Class = '__EventFilter'
$wmiParams.Arguments = @{
Name = 'WatchFiles'
EventNamespace = 'root\CIMV2'
QueryLanguage = 'WQL'
Query = $query
}
$filterResult = Set-WmiInstance @wmiParams
######################################################################################################################### Consumer
$wmiParams.Class = 'ActiveScriptEventConsumer'
$wmiParams.Arguments = @{
KillTimeout = 0
MachineName = $env:COMPUTERNAME
ScriptingEngine = 'VBScript'
ScriptText =
@"
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("c:\test\Log.log", 8, True)
objFile.WriteLine "hellohellohellohellohellohello"
objFile.Close
"@
ScriptFileName = $null
Name = 'ActiveScriptEventConsumer'
}
$consumerResult = Set-WmiInstance @wmiParams
######################################################################################################################### Binding
$wmiParams.Class = '__FilterToConsumerBinding'
$wmiParams.Arguments = @{
Filter = $filterResult
Consumer = $consumerResult
}
$bindingResult = Set-WmiInstance @wmiParams
Example MOF File :
#PRAGMA AUTOREOVER
#pragma namespace("\\\\.\\root\\subscription")
instance of __EventFilter as $EventFilter
{
Name = "Event Filter Instance Name";
EventNamespace = "Root\\Cimv2";
Query = "Select * From __InstanceCreationEvent Within 1 "
"Where TargetInstance Isa \"Cim_DirectoryContainsFile\" "
"and TargetInstance.GroupComponent=\"Win32_Directory.Name=\'C:\\\\test\'\"";
QueryLanguage = "WQL";
};
instance of ActiveScriptEventConsumer as $Consumer
{
Name = "TestConsumer";
ScriptingEngine = "VBScript";
ScriptFileName = "C:\\test\\test.vbs"
};
instance of __FilterToConsumerBinding
{
Filter = $EventFilter;
Consumer = $Consumer;
};
This seems like a good way of doing it, as I can set the interval myself, and working with WMI always feels safe to me as it is essentialy a built in solution in Windows.
Another way of doing this would be to write a script, something like :
$folderpath = 'C:\test'
$items = Get-ChildItem $folderpath
$currentdatetime = Get-Date
foreach($item in $items) {
If ($item.CreationTime > $currentdatetime.AddSeconds(-5)){
# Do some action
}
}
Which can then be run on the system as a scheduled task.
My question is - what is the best way of doing this? Of the 2 options I have shown, is one of them inherently more efficient in terms of system resources or potential for errors?
Are there any other ways of doing this which I have not considered?
Jisaak added an answer which uses System.IO.FilesystemWatcher
. Unfortunately this is not ideal for my purposes, as this only works while the shell which executed it is open, whereas I would like a more permanent solution (updated the title of the question to reflect this)
You don't want to long poll a folder for file changes, you should rather use a mechanism where you get notified when a file was created.
Therefore you can use the FileSystemWatcher
class and register to the created
event handler using the Register-ObjectEvent
cmdlet.
$folder = 'C:\test'
$filter = '*.*'
$fileSystemWatcher = New-Object IO.FileSystemWatcher $folder, $filter -Property @{
IncludeSubdirectories = $true # Set this according to your requirements.
NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'
}
$onCreated = Register-ObjectEvent $fileSystemWatcher Created -SourceIdentifier FileCreated -Action {
$path = $Event.SourceEventArgs.FullPath
Write-Host "File $Path was created".
}
Run this to unregister:
Unregister-Event -SourceIdentifier FileCreated
Based on the discussion here, it would seem that there shouldn't be any issues in terms of reliability or performance when using these types of events.
The impact of the ActiveScript consumer is trivial. If you do not create an event that fires continuously then the event is innocuous. I have used thises vevents for almosttwo decades without issue when designed correctly.
The WQL event filter DOES NOT POLL. It use SENS events to generate a response. SENS has been part of Windows since at least W2K. It is based in the kernel. A great amount of Windows is based on SENS.
They also mention that while PowerShell can use around 100MB to start up, WMI event monitoring does not (and neither does VBSCript - the preferred scripting language for ActiveScriptEvent
actions).
As I can't seem to find any information about this anywhere else (that is in terms of reliability/performance) I will have to go by this, and so will be implementing my folder monitor using WMI/WQL/MOF.
This article also has some useful information for anyone looking for more details. It also suggests that running this instead of some continuous application is a better use of system resources, however it doesn't mention whether this includes the use of scheduled tasks.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With