Writing Errors to Output Box

Hello,

I am trying to figure out how to get a function to write an error to an output box when a null value is put in. I have tried doing switch and try/catch but I couldn’t seem to get it to work.
The function looks like this:

function CurrentLoggedUser {
$computer=$InputBox.text;
$useron=gwmi win32_computersystem -comp $computer -erroraction 'silentlycontinue' | select -ExpandProperty UserName;
$outputbox.Text += $useron + [System.Environment]::NewLine}

the user inputs the computer name of an input box on a GUI and then the current user is displayed on an output box. As of now, if I don’t have the -erroraction ‘silentlycontinue’ the error write to the PowerShell window. I would like to know if it is possible to get the output that is displayed to the window, or have something like ‘Please input a correct pc name’ type of message display to the output box.

I’d start by reading “The Big Book of PowerShell Error Handling,” which is NOT big, is free, and is on our ebooks menu. That’ll help you understand how to “trap” and respond to the error. -EA SilentlyContinue, of course, means “ignore the error, I don’t want to know,” which isn’t what you want in this case. You actually want -EA Stop, along with an error-handling construct. The eBook has many examples, and you can also read the about_try_catch_finally topic in PowerShell’s own help. “Learn PowerShell Toolmaking in a Month of Lunches” also has a very solid chapter on error handling.

If you are in a GUI, I would use form validation on the field itself and have the Submit\Go\DoIt button disabled until form validation passed. Regardless of it being null, you need to do error handling if the computer isn’t reachable or any other failure occurs during the RPC call. Take a look at the error handling and logic here:

function CurrentLoggedUser {
    $computer=$InputBox.text;
    # if computer is not null
    if ($computer) {
        # ping computer prior to attempting WMI connection
        if (Test-Connection -ComputerName -Count 2 -Quiet) {
            try {
                #ErrorAction must be STOP to catch error
                $useron = Get-WmiObject -Class Win32_ComputerSystem -Computer $computer -ErrorAction Stop | 
                Select -ExpandProperty UserName
                # if useron is not null
                if ($useron) {
                    $message = $useron + [System.Environment]::NewLine
                }
                else {
                    $message = "Unable to get username from {0}" -f $computer
                }
            }
            catch {
                $message = "WMI Failure. {0}" -f $_
            }
        }
        else {
            $message = "{0} is offline" -f $computer
        }
    }
    else {
        $message = "Computer is null. Please enter a valid computername"
    }
    
    $outputbox.Text = $message
}

This is very interesting. I tried the if and try/catch statements themselves, but I didn’t think of putting them together in the same function. I appreciate your help very much. The only thing I had to change was adding $computer in Test-Connection -Computer $computer -Count 2 -Quiet.

I am going to use this and see what else I can do with it. I appreciate your help!

I agree with Don, you should read the “The Big Book of PowerShell Error Handling” it really helpful in understanding error handling.

As for your function you would want something like:

function CurrentLoggedUser {
$computer=$InputBox.text

Try{
    $useron=gwmi win32_computersystem -comp $computer | select -ExpandProperty UserName
    $outputbox.Text += $useron + [System.Environment]::NewLine
    }
catch{
    #I am guessing this is the message you want to add to the outputbox with the error
    $outputbox.text += $error[0].Exception
    }
}

I’m going to take a look at it today. I downloaded it yesterday but didn’t get a chance to start it. I have been looking for more resources for PowerShell since where I work is moving away from using batch files as much and more towards PowerShell. I haven’t used PS before this for the most part so I appreciate the help!

This function seems a lot easier to understand what is going on. I had tried the try/catch before posting but wasn’t sure what to put for the error message.

Note an error doesn’t stop a form. Forgot to mention what type of control $output box is. Is it a listbox,richtextbox,multiline textbox.


function Call-listboxerror_psf {

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

	#----------------------------------------------
	#region Generated Form Objects
	#----------------------------------------------
	[System.Windows.Forms.Application]::EnableVisualStyles()
	$form1 = New-Object 'System.Windows.Forms.Form'
	$button1 = New-Object 'System.Windows.Forms.Button'
	$textbox1 = New-Object 'System.Windows.Forms.TextBox'
	$listbox1 = New-Object 'System.Windows.Forms.ListBox'
	$InitialFormWindowState = New-Object 'System.Windows.Forms.FormWindowState'
	#endregion Generated Form Objects

	#----------------------------------------------
	# User Generated Script
	#----------------------------------------------
	
	$form1_Load={
		
		$textbox1.Text = 'boguscomputername'
		
	}
	
	#region Control Helper Functions
	function Load-ListBox 
	{
	
		Param (
			[ValidateNotNull()]
			[Parameter(Mandatory=$true)]
			[System.Windows.Forms.ListBox]$ListBox,
			[ValidateNotNull()]
			[Parameter(Mandatory=$true)]
			$Items,
		    [Parameter(Mandatory=$false)]
			[string]$DisplayMember,
			[switch]$Append
		)
		
		if(-not $Append)
		{
			$listBox.Items.Clear()	
		}
		
		if($Items -is [System.Windows.Forms.ListBox+ObjectCollection] -or $Items -is [System.Collections.ICollection])
		{
			$listBox.Items.AddRange($Items)
		}
		elseif ($Items -is [System.Collections.IEnumerable])
		{
			$listBox.BeginUpdate()
			foreach($obj in $Items)
			{
				$listBox.Items.Add($obj)
			}
			$listBox.EndUpdate()
		}
		else
		{
			$listBox.Items.Add($Items)	
		}
	
		$listBox.DisplayMember = $DisplayMember	
	}
	#endregion
	
	
	function get-CurrentLoggedUser {
		
		param($computer)
		
		Try {
			
			$useron = gwmi win32_computersystem -comp $computer -ea stop | select -ExpandProperty UserName
			#$outputbox.Text += $useron + [System.Environment]::NewLine
			
		} catch {
			
			#I am guessing this is the message you want to add to the outputbox with the error
			Load-ListBox $listbox1 "$computer : ($Error[0] -split "`n")" -Append
		}
	}
	$button1_Click={
		
		get-CurrentLoggedUser -computer $textbox1.text
		
	}
	
	# --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
		{
			$button1.remove_Click($button1_Click)
			$form1.remove_Load($form1_Load)
			$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($button1)
	$form1.Controls.Add($textbox1)
	$form1.Controls.Add($listbox1)
	$form1.AutoScaleDimensions = '6, 13'
	$form1.AutoScaleMode = 'Font'
	$form1.ClientSize = '745, 442'
	$form1.Name = 'form1'
	$form1.Text = 'Form'
	$form1.add_Load($form1_Load)
	#
	# button1
	#
	$button1.Location = '293, 80'
	$button1.Name = 'button1'
	$button1.Size = '75, 23'
	$button1.TabIndex = 2
	$button1.Text = 'button1'
	$button1.UseVisualStyleBackColor = $True
	$button1.add_Click($button1_Click)
	#
	# textbox1
	#
	$textbox1.Location = '80, 84'
	$textbox1.Name = 'textbox1'
	$textbox1.Size = '180, 20'
	$textbox1.TabIndex = 1
	#
	# listbox1
	#
	$listbox1.BackColor = 'Navy'
	$listbox1.Font = 'Microsoft Sans Serif, 12pt'
	$listbox1.ForeColor = 'Red'
	$listbox1.FormattingEnabled = $True
	$listbox1.ItemHeight = 20
	$listbox1.Location = '42, 190'
	$listbox1.Name = 'listbox1'
	$listbox1.Size = '644, 184'
	$listbox1.TabIndex = 0
	$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


It’s a Textbox. I changed the code Alex provided a little bit to look like:

function CurrentLoggedUser {

$computer=$InputBox.text

Try{
$ErrorActionPreference = ‘Stop’
$useron=gwmi win32_computersystem -comp $computer | select -ExpandProperty UserName
$outputbox.Text += $useron + [System.Environment]::NewLine
}
catch {
$outputbox.text += [System.Environment]::NewLine + “PC Not Found. Check that the Hostname or IP Address is Correct.”
{
}
}
}

my code didn’t like the error function he had, so I changed it to (‘Failed to access “{0}” : {1} in “{2}”’ -f $currentcomputer, ` $.Exception.Message, $.InvocationInfo.ScriptName)
But I figured that would be too hard for some people to understand so I changed it to a regular message.

The SAPIEN forms looks so much easier to use than how I did it. I don’t think my job would approve of us using it so I had to find an outline and go from there. Piecing together here and there. This is what it looks like though, any feedback is welcome. I know its a little sloppy.


[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")  

$tooltip1 = New-Object System.Windows.Forms.ToolTip

$Form = New-Object System.Windows.Forms.Form
$Form.Text = "BMDSS Multi-Tool"
$Form.Size = New-Object System.Drawing.Size(200,200)
$Form.AutoScalemode = "Dpi"
$Form.Autosize = $true
$Form.AutoSizeMode = "GrowAndShrink"

#region Generated Events
#----------------------------------------------
$Form_StateCorrection_Load=
{
#Correct the initial state of the form to prevent the .Net maximized form issue
	$Form.WindowState = $InitialFormWindowState
}
	$Form_Cleanup_FormClosed=
{
	#Remove all event handlers from the controls
	try
	{
			
	$Form.remove_Load($Form_Load)
	$Form.remove_Load($Form_StateCorrection_Load)
	$Form.remove_FormClosed($Form_Cleanup_FormClosed)
}
catch [Exception]
{ }
}
	#endregion Generated Events

################# Start functions  ##########################################################################################################

function userInfo {
if ($RadioButton1.Checked -eq $true) {Connect-RDP}
If ($RadioButton2.Checked -eq $true) {GetUptime}
If ($RadioButton3.Checked -eq $true) {PushPolicy}
If ($RadioButton4.Checked -eq $true) {GetHw}
If ($RadioButton5.Checked -eq $true) {CurrentLoggedUser}
If ($RadioButton1.Checked -eq $false -AND $RadioButton2.Checked -eq $false -AND $RadioButton3.Checked -eq $false -AND
$RadioButton4.Checked -eq $false -AND $RadioButton5.Checked -eq $false)
{
$outputBox.text += "Please Enter a Host Name or IP Address and Select an Option From the Radio Buttons." + [System.Environment]::NewLine
}
}

#Get Username of Current Logged in User
function CurrentLoggedUser {

$computer=$InputBox.text

Try{
    $ErrorActionPreference = 'Stop'
    $useron=gwmi win32_computersystem -comp $computer | select -ExpandProperty UserName
    $outputbox.Text += [System.Environment]::NewLine + $useron
    }
catch {
      $outputbox.text += [System.Environment]::NewLine + "PC Not Found.  Check that the Hostname or IP Address is Correct."
      {
   }
}
}
# End Current User
                     
#Start RDP
function Connect-RDP {
 $computer=$InputBox.text
$outputBox.text += [System.Environment]::NewLine + "Running RDP"
mstsc.exe /v $computer /f
}
#End RDP

#Admin Console Function Calls Batch - Checks for MMC if it exists opens it, if not creates new
function adminConsole  {
$scriptpath = $MyInvocation.MyCommand.Path
$dir = $PSScriptRoot
start-process "$dir\tools\runconsole.bat"
$outputBox.text += [System.Environment]::NewLine + "Admin Console will now  start in new window"
                     }   
#END Admin Console Function
                     
                     

################# Uptime Checks for different PS Versions ###################################################################################################################

 $UptimeSelection = 0
function GetUptime()
 {
 if ($PSVersionTable.PSVersion.Major -le 2)
 {
 $UptimeSelection = 1
 } 
 elseif ($PSVersionTable.PSVersion.Major -gt 2)
 {
 $UptimeSelection = 2
 }
 
 switch ($UptimeSelection) 
 {
#If Powershell Verison is 2 or lower it uses this uptime function.
1 {
    Try{
    $ErrorActionPreference = 'Stop'
$computer=$InputBox.text
$CSinfo = Get-WmiObject -ComputerName $computer -Query "SELECT LastBootUpTime FROM Win32_OperatingSystem"
$wmi=Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computer
$lastBoot=$wmi.ConvertToDateTime($wmi.LastBootUpTime)
$up=$wmi.ConvertToDateTime($wmi.LocalDateTime) - $wmi.ConvertToDateTime($wmi.LastBootUpTime)
$d=$up.days
$h=$up.hours
$m=$up.Minutes
$s=$up.Seconds
$outputbox.Text += "Uptime: $d Days $h Hours $m Min $s Sec" + [System.Environment]::NewLine + "Last boot: $lastBoot"
} catch {
      $outputbox.text += [System.Environment]::NewLine + "$computer Not Found.  Check that the Hostname or IP Address are correct and the power is on."}
      }
 
#If Poweshell Version is higher than Ver. 2 It uses this uptime function.     
2{
    Try{
    $ErrorActionPreference = 'Stop'     
$computer=$InputBox.text
$CSinfo = Get-CimInstance -classname Win32_OperatingSystem -ComputerName $computer  | Select-Object -expandproperty LastBootUpTime
$outputbox.Text += [System.Environment]::NewLine + "Uptime For: $computer - $CSinfo".ToUpper()
} catch {
      $outputbox.text += [System.Environment]::NewLine + "$computer Not Found.  Check that the Hostname or IP Address are correct and the power is on."}
        }
    }
}

################# End Uptime ###################################################################################################################

#Launch Command Prompt
 function GetCmd {
 Invoke-Item C:\Windows\System32\cmd.exe
$outputbox.Text += [System.Environment]::NewLine + "Launching Command Prompt"
}
#End Command Prompt

#Hardware Inventory - Updates the Date in SCCM - takesa few hours to update, but if you check the C:\Windows\CCM\Logs folder you can see that the time/date is updated
function GetHw {
 Try{
    $ErrorActionPreference = 'Stop'
    $computer=$InputBox.text
    Invoke-WMIMethod -ComputerName $computer -Namespace root\ccm -Class SMS_CLIENT -Name TriggerSchedule "{00000000-0000-0000-0000-000000000001}"
    $outputBox.text +=  [System.Environment]::NewLine + "Updating Hardware Inventory" 
 } catch {
    $outputBox.text +=  [System.Environment]::NewLine + "$computer not found.  Check the name and try again."}
 }
 #End HW Inv

#Push Policy - Updates Policy date is SCCM.  Takes ~5 min to update.
function PushPolicy{
Try{
    $ErrorActionPreference = 'Stop'
    $computer=$InputBox.text
    Invoke-WMIMethod -ComputerName $computer -Namespace root\ccm -Class SMS_CLIENT -Name TriggerSchedule  "{00000000-0000-0000-0000-000000000021}"
    $outputBox.text +=  [System.Environment]::NewLine +  "Pushing Policy"
} catch { 
    $outputBox.text +=  [System.Environment]::NewLine + "$computer not found.  Check the name and try again."}
 }
 #End Policy
################# end functions ###################################################################################################################


################ Start group boxes #################################################################################################################

$groupBox2 = New-Object System.Windows.Forms.GroupBox
$groupBox2.Location = New-Object System.Drawing.Size(175,25) 
$groupBox2.size = New-Object System.Drawing.Size(245,90) 
$groupBox2.text = "  Client Actions                SCCM Tools" 
$Form.Controls.Add($groupBox2) 

################# end group boxes ###################################################################################################################


################# Start radio buttons ###################################################################################################################

$RadioButton1 = New-Object System.Windows.Forms.RadioButton
$RadioButton1.Location = new-object System.Drawing.Point(15,15) 
$RadioButton1.size = New-Object System.Drawing.Size(75,20) 
$RadioButton1.Checked = $false
$RadioButton1.Text = "RDP"
$tooltip1.SetToolTip($RadioButton1, "RDP Into Machine")
$groupBox2.Controls.Add($RadioButton1) 

$RadioButton2 = New-Object System.Windows.Forms.RadioButton
$RadioButton2.Location = new-object System.Drawing.Point(15,35) 
$RadioButton2.size = New-Object System.Drawing.Size(75,20) 
$RadioButton2.Checked = $false
$RadioButton2.Text = "Uptime"
$tooltip1.SetToolTip($RadioButton2, "Get Uptime of PC")
$groupBox2.Controls.Add($RadioButton2) 

$RadioButton3 = New-Object System.Windows.Forms.RadioButton
$RadioButton3.Location = new-object System.Drawing.Point(115,35) 
$RadioButton3.size = New-Object System.Drawing.Size(100,20) 
$RadioButton3.Checked = $false
$RadioButton3.Text = "Push Policy"
$tooltip1.SetToolTip($RadioButton3, "Push Machine Policy from SCCM")
$groupBox2.Controls.Add($RadioButton3) 

$RadioButton4 = New-Object System.Windows.Forms.RadioButton
$RadioButton4.Location = new-object System.Drawing.Point(115,15) 
$RadioButton4.size = New-Object System.Drawing.Size(115,20) 
$RadioButton4.Checked = $false
$RadioButton4.Text = "HW Inventory"
$tooltip1.SetToolTip($RadioButton4, "Get Hardware Inventory")
$groupBox2.Controls.Add($RadioButton4) 

$RadioButton5 = New-Object System.Windows.Forms.RadioButton
$RadioButton5.Location = new-object System.Drawing.Point(15,55) 
$RadioButton5.size = New-Object System.Drawing.Size(100,30) 
$RadioButton5.Checked = $false
$RadioButton5.Text = "Current User"
$tooltip1.SetToolTip($RadioButton5, "Get Current Logged in User")
$groupBox2.Controls.Add($RadioButton5) 
################# end radio buttons ###################################################################################################################


################# Start text fields ###################################################################################################################

$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(15,30) 
$objLabel.Size = New-Object System.Drawing.Size(280,20) 
$objLabel.Text = "Enter Hostname or IP:"
$Form.Controls.Add($objLabel) 

$InputBox = New-Object System.Windows.Forms.TextBox 
$InputBox.Location = New-Object System.Drawing.Size(15,50) 
$InputBox.Size = New-Object System.Drawing.Size(125,20) 
$Form.Add_Shown({$Form.Activate(); $InputBox.focus()})
$Form.Controls.Add($InputBox) 

$outputBox = New-Object System.Windows.Forms.TextBox 
$outputBox.Location = New-Object System.Drawing.Size(10,150) 
$outputBox.Size = New-Object System.Drawing.Size(565,200) 
$outputBox.MultiLine = $True 
$outputBox.ScrollBars = "Vertical" 
$Form.Controls.Add($outputBox) 

################# end text fields ###################################################################################################################


################# Start buttons ###################################################################################################################

$Button = New-Object System.Windows.Forms.Button 
$Button.Location = New-Object System.Drawing.Size(460,20) 
$Button.Size = New-Object System.Drawing.Size(110,40) 
$Button.Text = "Execute" 
$Button.Add_Click({userInfo}) 
$Form.Controls.Add($Button) 

$ClearOutputButton = New-Object System.Windows.Forms.Button 
$ClearOutputButton.Location = New-Object System.Drawing.Size(10,128) 
$ClearOutputButton.Size = New-Object System.Drawing.Size(45,18) 
$ClearOutputButton.Text = "Clear" 
$ClearOutputButton.Add_Click({ClearOutputBox}) 
$tooltip1.SetToolTip($ClearOutputButton, "Clear The Output Box")
$Form.Controls.Add($ClearOutputButton) 

$Button2 = New-Object System.Windows.Forms.Button 
$Button2.Location = New-Object System.Drawing.Size(460,70) 
$Button2.Size = New-Object System.Drawing.Size(110,40) 
$Button2.Text = "Launch Admin Console" 
$tooltip1.SetToolTip($Button2, "Launch MMC")
$Button2.Add_Click({adminConsole}) 
$Form.Controls.Add($Button2) 

#Clear Output Box Button
function ClearOutputBox {
$outputbox.Text = ""
$RadioButton1.Checked = $false;
$RadioButton2.Checked = $false;
$RadioButton3.Checked = $false;
$RadioButton4.Checked = $false;
$RadioButton5.Checked = $false;
}
#End Clear Button

################# end buttons ###################################################################################################################

#Save the initial state of the form
$InitialFormWindowState = $Form.WindowState
#Init the OnLoad event to correct the initial state of the form
$Form.add_Load($Form_StateCorrection_Load)
#Clean up the control events
$Form.add_FormClosed($Form_Cleanup_FormClosed)
#Show the Form
$Form.Add_Shown({$Form.Activate()})
[void] $Form.ShowDialog()

Tell your job we could build that form in about 10 minutes and see if they go for it=D

Check this out for rdp within forms.

https://gallery.technet.microsoft.com/scriptcenter/Powershell-RDP-GUI-17e1aed0

I will look at that!

The only issue I have ran into so far is that it won’t pull an Uptime for some machines on the server but they are up because we can remote into them.

$psversiontablr is local. Cim or wmi depend on the remote system version. I would try cim then put wmi in that catch block. Either that or upgrade all clients or use wmi which works on both.

I wish I could upgrade the clients. Unfortunately it isn’t my call. Some/Most clients here run Ver 2.0, while a few run 4.0, doesn’t make any sense but I just changed the uptime to use WMI. I think last time I tried using cim on 2.0 it kicked can an error which is why I had the check in there, but I took it out and just went with WIM since seems to work for both.

Since I haven’t done much like this before, I’ve only done batch scripts, it’s pretty interesting how much you can actually do with PS… It’s been interesting.

I have yet to find anything you can’t do with it.