Need help detecting opening/closing of CD-ROM drive

Hi,

Very new to PowerShell and WMI. Watched Don Jones, “My Final 1-Day PowerShell v2 Workshop” and have a copy of the “Windows PowerShell Cookbook, Third Edition.”

I’m trying to write a PowerShell script that helps a user with copy and compare of a CD-ROM. Is there data in the top drive? Is there a blank CD-ROM in the lower drive? etc.

My plan is to

  1. subscribe to WMI events about the CD-ROM drives
  2. display a form prompting the user to do the right thing
  3. only enable the OK button when both drives meet the above conditions

Unfortunately, my event does not appear to be triggered by the opening or closing of the CD-ROM drive.

Am I listening for the wrong event? Am I not subscribing to events properly?

Any help would be appreciated.

##############################################################################
##
## Copy--CD.ps1
##
## From Select-GraphicalFilteredObject.ps1 in Windows PowerShell Cookbook (O'Reilly)
## by Lee Holmes (http://www.leeholmes.com/guide)
##
##############################################################################

 Copy-CD

#>

[CmdletBinding()]

Param()

Set-StrictMode -Version 2

Write-Verbose "Load the Windows Forms assembly"
Add-Type -Assembly System.Windows.Forms

Write-Verbose "Create the main form"
$form = New-Object Windows.Forms.Form
$form.Size = New-Object Drawing.Size @(300,300)

Write-Verbose "Create a label for status"
$label = New-Object Windows.Forms.Label
$label.Anchor = "Left"
$label.Text = "My status"
$label.Dock = "Top"

Write-Verbose "Create the button panel to hold the OK and Cancel buttons"
$buttonPanel = New-Object Windows.Forms.Panel
$buttonPanel.Size = New-Object Drawing.Size @(300,30)
$buttonPanel.Dock = "Bottom"

Write-Verbose "Create the Cancel button, which will anchor to the bottom right"
$cancelButton = New-Object Windows.Forms.Button
$cancelButton.Text = "Cancel"
$cancelButton.DialogResult = "Cancel"
$cancelButton.Top = $buttonPanel.Height - $cancelButton.Height - 5
$cancelButton.Left = $buttonPanel.Width - $cancelButton.Width - 10
$cancelButton.Anchor = "Right"

Write-Verbose "Create the OK button, which will anchor to the left of Cancel"
$okButton = New-Object Windows.Forms.Button
$okButton.Text = "Ok"
$okButton.DialogResult = "Ok"
$okButton.Enabled = $false
$okButton.Top = $cancelButton.Top
$okButton.Left = $cancelButton.Left - $okButton.Width - 5
$okButton.Anchor = "Right"

Write-Verbose "Add the buttons to the button panel"
$buttonPanel.Controls.Add($okButton)
$buttonPanel.Controls.Add($cancelButton)

function Get-CDDrives {
    @(Get-WMIobject win32_logicaldisk -filter 'DriveType=5' | Select Caption, Drive, MediaType, Access )
}

$drives = @(Get-CDDrives)

Write-Verbose $drives[0]
if ($drives[0].Access) {
    Write-Verbose $drives[0].Access
}
else {
    Write-Verbose "Drive is empty"
}

$dataCD = $drives[0].Access -eq 1

Write-Verbose $dataCD

$okButton.Enabled = $dataCD

if ($dataCD) {
    $label.Text = "Data CD ready."
}
else {
    $label.Text = "Please place the Data CD to be copied in the top tray."
}

Write-Verbose "Add the button panel and list box to the form, and also set"
Write-Verbose "the actions for the buttons"
$form.Controls.Add($label)
$form.Controls.Add($buttonPanel)
$form.AcceptButton = $okButton
$form.CancelButton = $cancelButton
$form.Add_Shown( { $form.Activate() } )

Write-Verbose "Listen for events."
$WMI = @{
    Query = "select * from __InstanceModificationEvent within 3 where TargetInstance ISA 'Win32_LogicalDisk'"
    Action = { $okButton.Enabled = $false }
}

Write-Verbose "Unregister any Event subscriber still hanging around"

Get-EventSubscriber | Unregister-Event
    
Register-WMIEvent @WMI

Write-Verbose "Show the form, and wait for the response"

$result = $form.ShowDialog()

function Copy-CD {
    Write-Verbose "Copying the CD..."    
}

if($result -eq "OK")
{
    Copy-CD
}
else {
    Write-Output "Never mind"
}

That’s because you’re listening for modifications on Win32_LogicalDisk, and the CD drive doesn’t “change” when it becomes empty or non-empty. It’s still a logical disk either way. PowerShell gets a bit tricky with event-driven stuff, because that wasn’t PowerShell’s first design goal.

https://support.microsoft.com/en-us/kb/163503 kind of covers the event Windows generates for insertion/deletion, but trapping that in PowerShell is probably going to be a lot harder than you thought. Get-Event might be a starting point, but I’m not sure how .NET-friendly that system-level event is going to be.

Is anything in ATA Learning Tutorials helpful?

From technet:
https://gallery.technet.microsoft.com/scriptcenter/EjectClose-CDDVD-drive-56d39361

And this works like a charm.

$Diskmaster = New-Object -ComObject IMAPI2.MsftDiscMaster2 
$DiskRecorder = New-Object -ComObject IMAPI2.MsftDiscRecorder2 
$DiskRecorder.InitializeDiscRecorder($DiskMaster) 
$DiskRecorder.EjectMedia()

Thank you for the speedy reply!

Several articles gave me hope. This article suggests that the CD-ROM insertion/removal can be detected with ManagementEventWatcher.

This article hints that Register-WMIEvent is the PowerShell 2.0 way to access System.Manager.ManagementEventWatcher.

The Windows PowerShell Cookbook equates(?) Win32_DeviceChangeEvent with WM_DEVICECHANGE.

Looking for Win32_DeviceChangeEvent using the all inclusive __InstanceOperationEvent still failed to notice the CD-ROM open/close.

$WMI = @{
    Query = "select * from __InstanceOperationEvent within 1 where TargetInstance ISA 'Win32_DeviceChangeEvent'"
    Action = { Check-Drive-Status }
}

Is it possible a PowerShell instance is not considered a top-level window and as such, won’t receive WM_DEVICECHANGE events?

PowerShell’s console host likely gets the event, but I doubt it’s passing it into the .NET stack. That was kinda my concern. PowerShell wasn’t really born for system-level event monitoring, per se. It’s WMI stuff is okay, but I’m not sure the WMI stack itself is a responder for that particular event. All that stuff is really designed for native code.

Thank you Chris.

The user will be the one opening and closing the drives.

PowerShell wants to detect when the drive opens or closes. Then PowerShell will go back and check to see if the user finally got a data CD in the top drive and a blank one in the bottom drive. At that point PowerShell will enable the OK button.

If events won’t work, PowerShell could occasionally poll until the user gets it right or cancels.

Alas, even my polling approach doesn’t work. Using some deep magic from the Windows PowerShell Cookbook, a simple timer should check the CD Drive every second.

The timer doesn’t appear to be running. No output from the timer is reported when ShowDialog is replaced with an infinite loop.

Again, your help would be much appreciated.

##############################################################################
##
## Copy-CD.ps1
##
## From Select-GraphicalFilteredObject.ps1 in Windows PowerShell Cookbook (O'Reilly)
## by Lee Holmes (http://www.leeholmes.com/guide)
##
##############################################################################

 Copy-CD

#>

[CmdletBinding()]

Param()

Set-StrictMode -Version 2

Write-Verbose "Load the Windows Forms assembly"
Add-Type -Assembly System.Windows.Forms

Write-Verbose "Create the main form"
$form = New-Object Windows.Forms.Form
$form.Size = New-Object Drawing.Size @(300,300)

Write-Verbose "Create a label for status"
$label = New-Object Windows.Forms.Label
$label.Anchor = "Left"
$label.Text = "My status"
$label.Dock = "Top"

Write-Verbose "Create the button panel to hold the OK and Cancel buttons"
$buttonPanel = New-Object Windows.Forms.Panel
$buttonPanel.Size = New-Object Drawing.Size @(300,30)
$buttonPanel.Dock = "Bottom"

Write-Verbose "Create the Cancel button, which will anchor to the bottom right"
$cancelButton = New-Object Windows.Forms.Button
$cancelButton.Text = "Cancel"
$cancelButton.DialogResult = "Cancel"
$cancelButton.Top = $buttonPanel.Height - $cancelButton.Height - 5
$cancelButton.Left = $buttonPanel.Width - $cancelButton.Width - 10
$cancelButton.Anchor = "Right"

Write-Verbose "Create the OK button, which will anchor to the left of Cancel"
$okButton = New-Object Windows.Forms.Button
$okButton.Text = "Ok"
$okButton.DialogResult = "Ok"
$okButton.Enabled = $false
$okButton.Top = $cancelButton.Top
$okButton.Left = $cancelButton.Left - $okButton.Width - 5
$okButton.Anchor = "Right"

Write-Verbose "Add the buttons to the button panel"
$buttonPanel.Controls.Add($okButton)
$buttonPanel.Controls.Add($cancelButton)

function Get-CDDrives {
    @(Get-WMIobject win32_logicaldisk -filter 'DriveType=5' | Select Caption, Drive, MediaType, Access )
}

$drives = @(Get-CDDrives)

Write-Verbose $drives[0]
if ($drives[0].Access) {
    Write-Verbose $drives[0].Access
}
else {
    Write-Verbose "Drive is empty"
}

function Check-Drive-Status {
    $dataCD = $drives[0].Access -eq 1

    Write-Verbose $dataCD

    $okButton.Enabled = $dataCD

    if ($dataCD) {
        $label.Text = "Data CD ready."
    }
    else {
        $label.Text = "Please place the Data CD to be copied in the top tray."
    }
}

Check-Drive-Status

Write-Verbose "Add the button panel and list box to the form, and also set"
Write-Verbose "the actions for the buttons"
$form.Controls.Add($label)
$form.Controls.Add($buttonPanel)
$form.AcceptButton = $okButton
$form.CancelButton = $cancelButton
$form.Add_Shown( { $form.Activate() } )

Write-Verbose "Unregister any Event subscriber still hanging around"

Get-EventSubscriber | Unregister-Event

Write-Verbose "Start a timer to check the CD."

$timer = New-Object Timers.Timer
$timer.Interval = 1000
$timer.AutoReset = $true

Register-ObjectEvent $timer Elapsed -SourceIdentifier Timer.Elapsed -Action { Write-Verbose "Pretending to check the CD" }
    
$timer.Enabled = $true
$timer.start()

Get-EventSubscriber

Write-Verbose "Show the form, and wait for the response"

while($true) { Write-Verbose "Processing loop"; Sleep 1 }

$result = $form.ShowDialog()

function Copy-CD {
    Write-Verbose "Copying the CD..."    
}

if($result -eq "OK")
{
    Copy-CD
}
else {
    Write-Output "Never mind"
}

How about just checking to see if media is loaded?

$VerbosePreference = 'Continue'
$Shell = New-Object -ComObject "Shell.Application"
Do {
    $cdrom = Get-WmiObject win32_cdromdrive
    if ($cdrom.MediaLoaded -eq 'True') {
        Write-Verbose 'Media found, checking type.'
        If ($cdrom.size) {
            Write-Verbose 'Data CD Found'
        } else {
            Write-Verbose 'Blank CD Found'
        }
        ($Shell.NameSpace(17).items() | Where {$_.Type -eq "CD Drive"}).InvokeVerb("Eject")
    }
    Start-Sleep -Seconds 1
} While ($true)

I loaded a CD with data on it first, then loaded a blank CD.

Results:

VERBOSE: Media found, checking type.
VERBOSE: Data CD Found
VERBOSE: Media found, checking type.
VERBOSE: Blank CD Found

Thanks to all for your help! Curtis’s approach to check CD drive status is an improvement over my original implementation.

The problem remained, how to check CD status while the form was displayed? This example provides the needed clue. Don’t use Timers.Timer, use System.Windows.Forms.Timer!

While this is working, there is still magic I don’t understand. What is the difference between “function CheckDriveStatus {…}” and “$CheckDriveStatus = {…}”? timer.addTick() only accepts $CheckDriveStatus not the function. Calling $CheckDriveStatus only echos the text of the code, but apparently doesn’t run the code.

##############################################################################
##
## Copy-CD.ps1
##
## From Select-GraphicalFilteredObject.ps1 in Windows PowerShell Cookbook (O'Reilly)
## by Lee Holmes (http://www.leeholmes.com/guide)
##
##############################################################################

 Copy-CD

#>

[CmdletBinding()]

Param()

Set-StrictMode -Version 2

Write-Verbose "Load the Windows Forms assembly"
Add-Type -Assembly System.Windows.Forms

Write-Verbose "Create the main form"
$form = New-Object Windows.Forms.Form
$form.Size = New-Object Drawing.Size @(300,300)

Write-Verbose "Create a label for status"
$label = New-Object Windows.Forms.Label
$label.Anchor = "Left"
$label.Text = "My status"
$label.Dock = "Top"

Write-Verbose "Create the button panel to hold the OK and Cancel buttons"
$buttonPanel = New-Object Windows.Forms.Panel
$buttonPanel.Size = New-Object Drawing.Size @(300,30)
$buttonPanel.Dock = "Bottom"

Write-Verbose "Create the Cancel button, which will anchor to the bottom right"
$cancelButton = New-Object Windows.Forms.Button
$cancelButton.Text = "Cancel"
$cancelButton.DialogResult = "Cancel"
$cancelButton.Top = $buttonPanel.Height - $cancelButton.Height - 5
$cancelButton.Left = $buttonPanel.Width - $cancelButton.Width - 10
$cancelButton.Anchor = "Right"

Write-Verbose "Create the OK button, which will anchor to the left of Cancel"
$okButton = New-Object Windows.Forms.Button
$okButton.Text = "Ok"
$okButton.DialogResult = "Ok"
$okButton.Enabled = $false
$okButton.Top = $cancelButton.Top
$okButton.Left = $cancelButton.Left - $okButton.Width - 5
$okButton.Anchor = "Right"

Write-Verbose "Add the buttons to the button panel"
$buttonPanel.Controls.Add($okButton)
$buttonPanel.Controls.Add($cancelButton)

$CheckDriveStatus = {
    $drives = @(Get-WMIObject win32_cdromdrive)
    
    $mediaLoaded = $drives[0].MediaLoaded

    $dataCD = $mediaLoaded -and $drives[0].size

    Write-Verbose $dataCD

    $okButton.Enabled = $dataCD

    if ($dataCD) {
        $label.Text = "The Data CD is ready."
    }
    elseif ($mediaLoaded) {
        $label.Text = "The Data CD in the top drive is blank."
    } 
    else {
        $label.Text = "Please place the Data CD to be copied in the top tray."
    }
}

Write-Verbose "Add the button panel and list box to the form, and also set"
Write-Verbose "the actions for the buttons"
$form.Controls.Add($label)
$form.Controls.Add($buttonPanel)
$form.AcceptButton = $okButton
$form.CancelButton = $cancelButton
$form.Add_Shown( { $form.Activate() } )

Write-Verbose "Start a timer to check the CD."

$timer = New-Object System.Windows.Forms.Timer

$timer.Interval  = 1000
$timer.Enabled   = $true
$timer.add_tick($CheckDriveStatus)
    
$timer.start()

Write-Verbose "Show the form, and wait for the response"

$result = $form.ShowDialog()

function Copy-CD {
    Write-Verbose "Copying the CD..."    
}

if($result -eq "OK")
{
    Copy-CD
}
else {
    Write-Output "Never mind"
}


#----------------------------------------------
#region Application Functions
#----------------------------------------------

#endregion Application Functions

#----------------------------------------------
# Generated Form Function
#----------------------------------------------
function Call-listboxerror_psf {

	#----------------------------------------------
	#region Import the Assemblies
	#----------------------------------------------
	[void][reflection.assembly]::Load('System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089')
	
	#endregion Import Assemblies

	#----------------------------------------------
	#region Generated Form Objects
	#----------------------------------------------
	[System.Windows.Forms.Application]::EnableVisualStyles()
	$form1 = New-Object 'System.Windows.Forms.Form'
	$labelCdromStatus = New-Object 'System.Windows.Forms.Label'
	$timer1 = New-Object 'System.Windows.Forms.Timer'
	$imagelist1 = New-Object 'System.Windows.Forms.ImageList'
	$InitialFormWindowState = New-Object 'System.Windows.Forms.FormWindowState'
	#endregion Generated Form Objects

	#----------------------------------------------
	# User Generated Script
	#----------------------------------------------
	
	$form1_Load={
		
		$timer1.start()
		
	}
	
	$timer1_Tick = {
		
		$status = (Get-WmiObject win32_logicaldisk -filter 'DriveType=5').access
		if ($status -eq 1) {$labelCdromStatus.ImageIndex = 0 } else { $labelCdromStatus.ImageIndex = 1 }
		
		
	}
	
	# --End User Generated Script--
	#----------------------------------------------
	#region Generated Events
	#----------------------------------------------
	
	$Form_StateCorrection_Load=
	{
		#Correct the initial state of the form to prevent the .Net maximized form issue
		$form1.WindowState = $InitialFormWindowState
	}
	
	$Form_Cleanup_FormClosed=
	{
		#Remove all event handlers from the controls
		try
		{
			$form1.remove_Load($form1_Load)
			$timer1.remove_Tick($timer1_Tick)
			$form1.remove_Load($Form_StateCorrection_Load)
			$form1.remove_FormClosed($Form_Cleanup_FormClosed)
		}
		catch [Exception]
		{ }
	}
	#endregion Generated Events

	#----------------------------------------------
	#region Generated Form Code
	#----------------------------------------------
	$form1.SuspendLayout()
	#
	# form1
	#
	$form1.Controls.Add($labelCdromStatus)
	$form1.AutoScaleDimensions = '6, 13'
	$form1.AutoScaleMode = 'Font'
	$form1.ClientSize = '196, 111'
	$form1.Name = 'form1'
	$form1.Text = 'Form'
	$form1.add_Load($form1_Load)
	#
	# labelCdromStatus
	#
	$labelCdromStatus.ImageAlign = 'MiddleLeft'
	$labelCdromStatus.ImageIndex = 0
	$labelCdromStatus.ImageList = $imagelist1
	$labelCdromStatus.Location = '33, 35'
	$labelCdromStatus.Name = 'labelCdromStatus'
	$labelCdromStatus.Size = '112, 23'
	$labelCdromStatus.TabIndex = 0
	$labelCdromStatus.Text = 'cd-rom status'
	$labelCdromStatus.TextAlign = 'MiddleCenter'
	#
	# timer1
	#
	$timer1.add_Tick($timer1_Tick)
	#
	# imagelist1
	#
	$Formatter_binaryFomatter = New-Object System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
	#region Binary Data
	$System_IO_MemoryStream = New-Object System.IO.MemoryStream (,[byte[]][System.Convert]::FromBase64String('
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAu
MC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAA
ACZTeXN0ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkD
AAAADwMAAACACQAAAk1TRnQBSQFMAgEBAgEAAQgBAAEIAQABEAEAARABAAT/AQkBAAj/AUIBTQE2
AQQGAAE2AQQCAAEoAwABQAMAARADAAEBAQABCAYAAQQYAAGAAgABgAMAAoABAAGAAwABgAEAAYAB
AAKAAgADwAEAAcAB3AHAAQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEA
AykBAANVAQADTQEAA0IBAAM5AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8B
AAHWAucBAAGQAakBrQIAAf8BMwMAAWYDAAGZAwABzAIAATMDAAIzAgABMwFmAgABMwGZAgABMwHM
AgABMwH/AgABZgMAAWYBMwIAAmYCAAFmAZkCAAFmAcwCAAFmAf8CAAGZAwABmQEzAgABmQFmAgAC
mQIAAZkBzAIAAZkB/wIAAcwDAAHMATMCAAHMAWYCAAHMAZkCAALMAgABzAH/AgAB/wFmAgAB/wGZ
AgAB/wHMAQABMwH/AgAB/wEAATMBAAEzAQABZgEAATMBAAGZAQABMwEAAcwBAAEzAQAB/wEAAf8B
MwIAAzMBAAIzAWYBAAIzAZkBAAIzAcwBAAIzAf8BAAEzAWYCAAEzAWYBMwEAATMCZgEAATMBZgGZ
AQABMwFmAcwBAAEzAWYB/wEAATMBmQIAATMBmQEzAQABMwGZAWYBAAEzApkBAAEzAZkBzAEAATMB
mQH/AQABMwHMAgABMwHMATMBAAEzAcwBZgEAATMBzAGZAQABMwLMAQABMwHMAf8BAAEzAf8BMwEA
ATMB/wFmAQABMwH/AZkBAAEzAf8BzAEAATMC/wEAAWYDAAFmAQABMwEAAWYBAAFmAQABZgEAAZkB
AAFmAQABzAEAAWYBAAH/AQABZgEzAgABZgIzAQABZgEzAWYBAAFmATMBmQEAAWYBMwHMAQABZgEz
Af8BAAJmAgACZgEzAQADZgEAAmYBmQEAAmYBzAEAAWYBmQIAAWYBmQEzAQABZgGZAWYBAAFmApkB
AAFmAZkBzAEAAWYBmQH/AQABZgHMAgABZgHMATMBAAFmAcwBmQEAAWYCzAEAAWYBzAH/AQABZgH/
AgABZgH/ATMBAAFmAf8BmQEAAWYB/wHMAQABzAEAAf8BAAH/AQABzAEAApkCAAGZATMBmQEAAZkB
AAGZAQABmQEAAcwBAAGZAwABmQIzAQABmQEAAWYBAAGZATMBzAEAAZkBAAH/AQABmQFmAgABmQFm
ATMBAAGZATMBZgEAAZkBZgGZAQABmQFmAcwBAAGZATMB/wEAApkBMwEAApkBZgEAA5kBAAKZAcwB
AAKZAf8BAAGZAcwCAAGZAcwBMwEAAWYBzAFmAQABmQHMAZkBAAGZAswBAAGZAcwB/wEAAZkB/wIA
AZkB/wEzAQABmQHMAWYBAAGZAf8BmQEAAZkB/wHMAQABmQL/AQABzAMAAZkBAAEzAQABzAEAAWYB
AAHMAQABmQEAAcwBAAHMAQABmQEzAgABzAIzAQABzAEzAWYBAAHMATMBmQEAAcwBMwHMAQABzAEz
Af8BAAHMAWYCAAHMAWYBMwEAAZkCZgEAAcwBZgGZAQABzAFmAcwBAAGZAWYB/wEAAcwBmQIAAcwB
mQEzAQABzAGZAWYBAAHMApkBAAHMAZkBzAEAAcwBmQH/AQACzAIAAswBMwEAAswBZgEAAswBmQEA
A8wBAALMAf8BAAHMAf8CAAHMAf8BMwEAAZkB/wFmAQABzAH/AZkBAAHMAf8BzAEAAcwC/wEAAcwB
AAEzAQAB/wEAAWYBAAH/AQABmQEAAcwBMwIAAf8CMwEAAf8BMwFmAQAB/wEzAZkBAAH/ATMBzAEA
Af8BMwH/AQAB/wFmAgAB/wFmATMBAAHMAmYBAAH/AWYBmQEAAf8BZgHMAQABzAFmAf8BAAH/AZkC
AAH/AZkBMwEAAf8BmQFmAQAB/wKZAQAB/wGZAcwBAAH/AZkB/wEAAf8BzAIAAf8BzAEzAQAB/wHM
AWYBAAH/AcwBmQEAAf8CzAEAAf8BzAH/AQAC/wEzAQABzAH/AWYBAAL/AZkBAAL/AcwBAAJmAf8B
AAFmAf8BZgEAAWYC/wEAAf8CZgEAAf8BZgH/AQAC/wFmAQABIQEAAaUBAANfAQADdwEAA4YBAAOW
AQADywEAA7IBAAPXAQAD3QEAA+MBAAPqAQAD8QEAA/gBAAHwAfsB/wEAAaQCoAEAA4ADAAH/AgAB
/wMAAv8BAAH/AwAB/wEAAf8BAAL/AgAD/wUAAf8BBwFJAQIBJwFJAesB8QH/AwABGgZZCFIBdCMA
AfABSQKXAlYBlwF4AU8BbQH0AgABMgF6BVkBFQEQAUsCWQNTATEiAAHvAXIBVQE0BFUBNAFVAXIB
SQH0AQABUwF6BFkBWAEVAREBSwJZA1MBTCEAAfMBcgItAS4DNAMuAS0BcgFtAf8BAAZZAVEBbQFS
AlkDUwEaIQABSQFPAy0B9gH/AVUELQFPAUkB8QEAAZoBegRZAkoBUgJZAlMBKwH1IAAB8wGXAy0B
9AP/BS0BlwHrAQAB9QEyAXoDWQERARABSwJZAVMBUgHzIQAB7QFVAi0BGwH/AfQB9QH/AfQBTwEu
Ai0BVQFJAgABGgF6A1kCEQFLA1kBdCIAAUkBLgFVAZgB/wH0AVYBTwL/A5cBVQEuAScDAAKaAXoB
WQEVAUMBSwJZATEBGyIAAUkBVgKYAXIBUAGXAVYBcgL/AXgCmAFWAScDAAEbA3oBEwEVAUsCegGZ
IwAB7QGXBJgDlwHyAv8CmAGXAUkEAAFTAZoBegHrAW0BUQF6AXQkAAHzAZcGmAJ4Av8B8wGYAZcB
BwUAAnoBcwHtAXQBegEaJQABSQmYAf8BCAGYAUkB/wUAARoBmgN6AVImAAHzAXIKmAFyAfAGAAH2
AXQBmgJ6AfQnAAEIAXIImAFyAe8JAAF0AXoBGikAAfMBSQKXApgClwFJAfMKAAH/AfMsAAHzAe0C
SQHtAfM1AAFCAU0BPgcAAT4DAAEoAwABQAMAARADAAEBAQABAQUAAYAXAAP/AQAB8AEHBgAB4AED
BgABwAEBBgABgAEAAYAFAAGAAQABgAcAAYABAQYAAcABAwYAAeABAwYAAeABBwYAAfABDwYAAfgB
DwQAAYABAAH4AR8EAAGAAQEB+AEfBAABwAEDAf4BPwQAAeABBwH+AX8EAAH4AR8C/wQACw=='))
	#endregion
	$imagelist1.ImageStream = $Formatter_binaryFomatter.Deserialize($System_IO_MemoryStream)
	$Formatter_binaryFomatter = $null
	$System_IO_MemoryStream = $null
	$imagelist1.TransparentColor = 'Transparent'
	$form1.ResumeLayout()
	#endregion Generated Form Code

	#----------------------------------------------

	#Save the initial state of the form
	$InitialFormWindowState = $form1.WindowState
	#Init the OnLoad event to correct the initial state of the form
	$form1.add_Load($Form_StateCorrection_Load)
	#Clean up the control events
	$form1.add_FormClosed($Form_Cleanup_FormClosed)
	#Show the Form
	return $form1.ShowDialog()

} #End Function

#Call the form
Call-listboxerror_psf | Out-Null

The only CD I have on my desk is for MCSE Boot Camp 1998 - TCP/IP for Windows NT 4.0 =D