Hey, Scripting Guy! Is there any way to monitor a folder for file creations and deletions, all with just one script?
— HA
Hey, HA. By the way, thanks for the question: you temporarily saved us from answering a different question, one that was proving a bit difficult thanks to some surprising differences between Windows XP Service Pack 1 and Windows XP Service Pack 2. (We’ll tackle that question in tomorrow’s column.) Your question, by contrast, is an easy one to answer, even if most people aren’t aware of that.
Most people are aware that you can monitor for file creations, deletions, or modifications by using one of these three event classes (note the two underscores at the beginning of all three class names):
• |
__InstanceCreationEvent. This is used for monitoring creation events; that is, you can be notified any time a new instance of something gets created. In our case, notification will occur any time a new file is created in the folder C:\Scripts. |
• |
__InstanceDeletionEvent. You’re way ahead of us: this class is used for monitoring deletion events; we’ll be notified any time a file is deleted from C:\Scripts. |
• |
__InstanceModificationEvent. Finally, we can be notified any time a file in C:\Scripts is modified in some way (you change the file name, you add or delete something from the file, etc.). |
Note. If none of this makes sense to you, don’t worry; instead, check out this WMI Events webcast from Scripting Week 2. In that webcast we discuss WMI events in detail, and we explain things like ExecNotificationQuery and WITHIN 10. |
Set colMonitoredEvents = objWMIService.ExecNotificationQuery _ (“SELECT * FROM __InstanceCreationEvent WITHIN 10 WHERE ” _ & “Targetinstance ISA ‘CIM_DirectoryContainsFile’ and ” _ & “TargetInstance.GroupComponent= ” _ & “‘Win32_Directory.Name=””c:\\\\scripts””‘”)
However, this next query – which tries to monitor instances of both the __InstanceCreationEvent and __InstanceDeletionEvent classes – is doomed to fail:
Set colMonitoredEvents = objWMIService.ExecNotificationQuery _ (“SELECT * FROM __InstanceCreationEvent OR __InstanceDeletionEvent WITHIN 10 WHERE ” _ & “Targetinstance ISA ‘CIM_DirectoryContainsFile’ and ” _ & “TargetInstance.GroupComponent= ” _ & “‘Win32_Directory.Name=””c:\\\\scripts””‘”)
You just can’t do it; instead, you get the terse error message: “Unparsable query.” Yikes.
Is this a problem? Of course it is. Although there are exceptions, you’ll often want to know about any activity that goes on in C:\Scripts, not just file creations or file deletions. Because of that people often resort to writing – and running – separate scripts to monitor and report on each of the WMI event classes.
But guess what: there’s a better way. Turns out there’s actually a fourth WMI event class: __InstanceOperationEvent. Best of all, our other three event classes are all subsumed under __InstanceOperationEvent; you can use __InstanceOperationEvent to monitor for event creations, event deletions, and event modifications. We’ll use it today to monitor for both file creations and deletions.
What we do is monitor for instances of the __InstanceOperationEvent class; new instances of this class are created pretty much any time anything occurs on a computer. We then check the value of the Path_.Class property to determine whether the new instance was created in response to a file creation or file deletion. That’s how we can monitor for file creations and deletions in a single script.
Let’s take a look at the code:
strComputer = “.” Set objWMIService = GetObject(“winmgmts:\\” & strComputer & “\root\cimv2”)Set colMonitoredEvents = objWMIService.ExecNotificationQuery _ (“SELECT * FROM __InstanceOperationEvent WITHIN 10 WHERE ” _ & “Targetinstance ISA ‘CIM_DirectoryContainsFile’ and ” _ & “TargetInstance.GroupComponent= ” _ & “‘Win32_Directory.Name=””c:\\\\scripts””‘”)
Do While TRUE Set objEventObject = colMonitoredEvents.NextEvent()
Select Case objEventObject.Path_.Class Case “__InstanceCreationEvent” Wscript.Echo “A new file was just created: ” & _ objEventObject.TargetInstance.PartComponent Case “__InstanceDeletionEvent” Wscript.Echo “A file was just deleted: ” & _ objEventObject.TargetInstance.PartComponent End Select Loop
Keep in mind that looks can be deceiving here; the script isn’t quite as complicated as it might first appear. All we’re really doing here is using a basic ExecNotificationQuery to check every 10 seconds (WITHIN 10) for new instances of the __InstanceOperationEvent class; in particular, we’re looking for instances involving the folder C:\Scripts. That’s what this crazy piece of code does:
Set colMonitoredEvents = objWMIService.ExecNotificationQuery _ (“SELECT * FROM __InstanceOperationEvent WITHIN 10 WHERE ” _ & “Targetinstance ISA ‘CIM_DirectoryContainsFile’ and ” _ & “TargetInstance.GroupComponent= ” _ & “‘Win32_Directory.Name=””c:\\\\scripts””‘”)
Note. If none of this makes sense to you, don’t worry; instead, check out this WMI Events webcast from Scripting Week 2. In that webcast we discuss WMI events in detail, and we explain things like ExecNotificationQuery and WITHIN 10.
After issuing the query we simply create a Do Loop and wait for the next event to occur. When it does we use the Path_.Class property to determine the event type; we then echo a message that an event just took place. This message also includes the WMI object path for the new instance (information which happens to be stored in the PartComponent property). The object path for a file looks something like this:
\\ATL-WS-01\root\cimv2:CIM_DataFile.Name=”c:\\scripts\\New Text Document.txt”
We didn’t bother doing so here, but you could add a few lines of code to the script to pull out just the file path (in this case, C:\Scripts\New Text Document.txt). Otherwise, you’ll just have to make do with the WMI object path.
Here’s where all that excitement takes place:
Select Case objEventObject.Path_.Class Case “__InstanceCreationEvent” Wscript.Echo “A new file was just created: ” & _ objEventObject.TargetInstance.PartComponent Case “__InstanceDeletionEvent” Wscript.Echo “A file was just deleted: ” & _ objEventObject.TargetInstance.PartComponent End Select
That’s basically it; we then loop around and wait for the next event. And while we’re waiting, we’ll get back to work on that other question, the one that inspired us to take the easy way out and answer this question instead.
0 comments