Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Most Efficient and Reliable Way to Permanently Monitor File Creation in a Specific Folder

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)

like image 493
Bassie Avatar asked Apr 27 '16 11:04

Bassie


2 Answers

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
like image 98
Martin Brandl Avatar answered Nov 16 '22 21:11

Martin Brandl


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.

like image 45
Bassie Avatar answered Nov 16 '22 23:11

Bassie