November 3rd, 2009

Hey, Scripting Guy! Can I Be Informed When a Portable Drive Is Added by My Computer?

Bookmark and Share

 

Hey, Scripting Guy! Question

Hey, Scripting Guy! I would love to be able to write a Windows PowerShell script that will inform me when a portable drive is added to my computer. Do you have ideas you could suggest?

— RC

Hey, Scripting Guy! AnswerHello RC,

Microsoft Scripting Guy Ed Wilson here. Well it has already been a rather interesting week. I bought a new computer and loaded Windows 7 over the weekend. My new computer is hot. I loaded it up with 8 GB of RAM, 4 processor cores, and a solid-state boot drive for the operating system. I practically need to bolt it to the floor because the thing really flies. Of course, the Scripting Wife was unimpressed when I told her I needed to also purchase an ESATA array (for all my scuba-diving pictures). So I had to bribe her with a new flat-panel monitor for her Windows 7 Ultimate computer downstairs. Each of my high-resolution pictures is around 10 MB in size, and on my old computer scrolling, through the pictures was frustrating as I had to wait several seconds for each picture to load. On my new computer, I can click through the pictures as fast as I wish with no appreciable lag time. It makes reliving some of my old dives much more pleasant.

As an example, the following picture is a green moray eel I found hiding on the shipwreck Sea Tiger off the coast of Honolulu, Hawaii, in the United States. That dive was in June 2008 with a maximum depth of 110 feet (33 meters). The visibility was over 100 feet (30 meters), and the water was 81 degrees Fahrenheit (27 degrees Celsius). I was over there teaching a Windows PowerShell class and decided to combine a bit of holiday while I was on the island. All in all it was a lovely dive—and I get to relive it because of my new Windows 7 computer.

Image of green moray eel

RC, you want to be notified when an external drive (such as a USB drive) is attached to your computer. Using Windows PowerShell 2.0 (in the box on Windows 7 and Windows 2008 Server R2; downloadable for other operating systems), I wrote the MonitorDriveEvents.ps1 script. This event-driven script is shown here.

MonitorDriveEvents.ps1

#requires -version 2.0

Function Get-EventType($eventType)

{

 switch ($eventType)

 {

  1 {“Configuration changed”}

  2 {“Device arrival”}

  3 {“Device removal”}

  4 {“docking”}

 } #end switch

} #end get-eventType function

 

$MaxEvents = 5

$ErrorActionPreference = “stop”

Register-WmiEvent -Class win32_VolumeChangeEvent -SourceIdentifier volumeChange

 

For($i = 1 ; $i -le $MaxEvents ; $i ++)

{

 Try {

 $newEvent = Get-Event -SourceIdentifier volumeChange

 write-host “Drive: ” $newEvent.SourceEventArgs.NewEvent.DriveName “changed on $env:COMPUTERNAME”

 write-host “Drive event was ” $(Get-EventType -eventType $newEvent.SourceEventArgs.NewEvent.EventType)

 Remove-Event -SourceIdentifier volumeChange }

 Catch [System.Management.Automation.RuntimeException]{“No events”}

 Start-Sleep -Seconds 5

}

The MonitorDriveEvents.ps1 script begins by using the #requires –version 2.0 tag. The #requires statement looks like it is a comment that would be ignored by Windows PowerShell. However, the #requires statement is a directive that tells Windows PowerShell that the script will run only on Windows PowerShell 2.0. If you attempt to run a script that includes the #requires –version 2.0 directive on Windows PowerShell 1.0, the script will fail. The error message that is produced is seen here:

Image of error message when using requires statement on Windows PowerShell 1.0


The next thing that is done in the MonitorDriveEvents.ps1 script is to create the Get-EventType function. The Get-EventType function receives the eventType property when an event is generated. The eventType property is defined on the Win32_VolumeChangeEvent WMI class. This class is documented on MSDN and has two properties that we are interested in using. The first property is the drive letter that is assigned to the newly arrived drive, and the second is the eventType property from the instance of the Win32_VolumeChangedEvent WMI class that is created when an event arrives. This property lets us know if the drive has been added to the system or removed from the system. Because the eventType property is a numeric coded value, we need to translate the number that is returned into a string that makes more sense to us when the script is run. The switch statement in Windows PowerShell is similar to the Select-Case statement from VBScript (although it is more powerful, and can do more than choose between different input values). You can also think of the switch statement as a series of if statements. “If the value of the $eventType input variable is equal to 1, then output ‘Configuration changed’.” The Get-EventType function is seen here:

Function Get-EventType($eventType)

{

 switch ($eventType)

 {

  1 {“Configuration changed”}

  2 {“Device arrival”}

  3 {“Device removal”}

  4 {“docking”}

 } #end switch

} #end get-eventType function

After the Get-EventType function has been created, you come to the entry point to the script. Two variables receive values. The first variable, $MaxEvents, is used to determine how many event cycles will be examined. The second variable, $ErrorActionPreference, is a system variable that governs the way the script will behave if an error is detected. By setting the $ErrorActionPreference to stop, errors will cause the script to stop. This stop condition will be handled later in the script. Here are the two lines of code that assign values to variables:

$MaxEvents = 5

$ErrorActionPreference = “stop”

To create a registration for the WMI event, you use the Register-WmiEvent Windows PowerShell cmdlet. Because the Win32_VolumeChangeEvent WMI class is designed to raise events, no special constructions are required to use the class—you can use the WMI class directly with the cmdlet. The SourceIdentifier parameter is used to give a name to the event source that will be used later to retrieve WMI events. This line of code is shown here:

Register-WmiEvent -Class win32_VolumeChangeEvent -SourceIdentifier volumeChange

A For loop is used to check for events a certain number of times. The $MaxEvents variable controls how many loops will be made to check for new WMI events. This is shown here:

For($i = 1 ; $i -le $MaxEvents ; $i ++)

{

A Try…Catch construction is used to check for new events and to remove events from the WMI event queue. If you use the Get-Event cmdlet and there are no events, an error is generated. To avoid this, the Try…Catch construction is used. This is new syntax for Windows PowerShell 2.0. The Windows PowerShell script will attempt to retrieve new events by using the Get-Event cmdlet. If an error is generated, it is trapped by the Catch statement. Because the $ErrorActionPreference was set to stop, any error that is generated inside the Try statement will be caught by the Catch statement. If no errors are generated, the new events that are in the WMI event queue, are stored in the $newEvent variable. The Write-Host cmdlet is used to display the drive name from the new event and the event type. The value of the EventType is translated by calling the Get-EventType function. After this has been done, the event is removed by using the Remove-Event cmdlet. This section of the script is shown here:

Try {

 $newEvent = Get-Event -SourceIdentifier volumeChange

 write-host “Drive: ” $newEvent.SourceEventArgs.NewEvent.DriveName “changed on $env:COMPUTERNAME”

 write-host “Drive event was ” $(Get-EventType -eventType $newEvent.SourceEventArgs.NewEvent.EventType)

 Remove-Event -SourceIdentifier volumeChange }

The Catch statement is used to catch any Windows PowerShell errors. If an error occurs when reading the event from the event queue, it means that no event has occurred. The phrase “No events” is displayed if an error is detected. The Start-Sleep cmdlet is used to pause the execution of the script for five seconds. This section of the script is shown here:

Catch [System.Management.Automation.RuntimeException]{“No events”}

 Start-Sleep -Seconds 5

}

When the script runs, the drive letter of newly added drives is displayed in the output pane of the Windows PowerShell ISE. If no new drive is detected, the phrase “No events” is displayed. The output from the Windows PowerShell ISE is seen here:

Image of output from Windows PowerShell ISE


Well, RC, that is about all there is to checking for a drive event. Keep in mind that the Windows PowerShell script needs to be running to detect if a drive has been inserted. In addition, because of the way the script is written, it will only display information for one event per polling cycle (the amount of time the script is sleeping). If you have multiple events that occur during the polling cycle, you would need to use either a For statement or a ForEach statement to iterate through the collection of events that are returned and display the drive information from the event. This script is not intended to be a security auditing script, but rather a convenience script you could easily modify to automatically transfer files from a folder to the portable drive in an automated fashion. Join us tomorrow as WMI Event Week continues.

If you want to know exactly what we will be looking at tomorrow, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at scripter@microsoft.com or post them on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys

 

Author

0 comments

Discussion are closed.