Powershell:- Raising an ArgumentException

Background

Good software engineers engage in defensive development.

As opposed to offensive practices.

Code

In this post, we will employ .Net’s System.ArgumentException to offer examples of how to better help the developer know why an application is failing.

Outline

  1. Function:- calculatorGraceful
    • If operation is equal and divisor is 0
      • If exceptionType is 1
        • throw (New-Object -TypeName System.ArgumentExceptionArgumentList “$log”);
      • If exceptionType is 2
        • throw (New-Object -TypeName System.ArgumentExceptionArgumentList “$log”, “number2” );
      • Else
        • Write-Warning

Script

Set-StrictMode -Version Latest

$script:ErrorActionPreference = "Stop"

[int] $number1 = 0;
[int] $number2 = 0;

[int] $numberAlt1 = 0;
[int] $numberAlt2 = 0;

[float]  $result  = 0;
[string] $operator = "";
[string] $logInternal = "";

Set-Variable FORMAT_OUTPUT -option Constant -value `
				"`t{0} {1} {2} is {3}";
 
Set-Variable FORMAT_MESSAGE_DIVISOR_CANNOT_BE_ZERO -option Constant -value `
				"`t`tIn Module {0}, Function {1}:- number2 ({2}) is invalid.  It can not be zero when operator is {3}";
				
[string] $log = "";

[boolean] $bErrorHandling = $false;

[int]     $exceptionType =0;

[char] $dividingChar = '=';	
[int]  $dividingLength = 80;

[string] $moduleName = "";
[string] $functionName = "";


<#
	Please set the boolean variable bErrorHandling off or on depending on whether you want to engage exception handling
#>
$bErrorHandling = $false;
$bErrorHandling = $true;

<#
	Get the name of the script
	
	https://stackoverflow.com/questions/817198/how-can-i-get-the-current-powershell-executing-file
#>
function Get-ScriptFileNameFull 
{
	
	[object] $scriptInvocation= $null;
	[string] $scriptName = "";
	
	# Get the MyInvocation variable at script level
	# Can be done anywhere within a script
	$ScriptInvocation = (Get-Variable MyInvocation -Scope Script).Value

	# Get the full path to the script
	$ScriptPath = $ScriptInvocation.MyCommand.Path
		
}

<#
	Get the name of the script
	
	https://stackoverflow.com/questions/817198/how-can-i-get-the-current-powershell-executing-file
#>
function Get-ScriptFileName
{
	
	[object] $scriptInvocation= $null;
	[string] $scriptName = "";
	
	# Get the MyInvocation variable at script level
	# Can be done anywhere within a script
	$scriptInvocation = (Get-Variable MyInvocation -Scope Script).Value
	
	# Get the script name
	# Yes, could get via Split-Path, but this is "simpler" since this is the default return value
	$scriptName = $ScriptInvocation.MyCommand.Name
	
	return ($scriptName);
	
}


<#
	Is there a way to retrieve a PowerShell function name from within a function?

	https://stackoverflow.com/questions/3689543/is-there-a-way-to-retrieve-a-powershell-function-name-from-within-a-function
#>
function Get-FunctionName ([int]$StackNumber = 1) 
{
    return [string]$(Get-PSCallStack)[$StackNumber].FunctionName
	
}

<#

	Relies on 3rd party code

	A) Quick tip I found for adjusting console color for "Write-Output"
	   

#>
function writeDivider([char] $ch, [int] $length)
{
	
	[string] $log = "";
	[string] $Foreground = "";
	
	$log = New-Object -TypeName String -ArgumentList $ch, $length;	

	# Saves current foreground color to $Foreground
    $Foreground = $host.ui.RawUI.ForegroundColor;
	
	# Changes foreground color to red
    $host.ui.RawUI.ForegroundColor = "Green";
	 
	Write-Output $log;
	
	# Returns the foreground color to it's original state
    $host.ui.RawUI.ForegroundColor = $Foreground
	
}

function calculator([string] $operator, [int] $number1, [int] $number2)
{
	
	[float] $result =0;
	
	if ($operator -eq "+" )
	{
		$result = $number1 + $number2;
	}
	elseif ($operator -eq "-" )
	{
		$result = $number1 - $number2;
	}	
	elseif ($operator -eq "*" )
	{
		$result = $number1 * $number2;
	}	
	elseif ($operator -eq "/" )
	{
		$result = $number1 / $number2;
	}
	elseif ($operator -eq "%" )
	{
		$result = $number1 % $number2;
	}
		
	
	return ($result);
	
}

function calculatorGraceful( `
	[string] $operator, `
	[int] $number1, `
	[int] $number2, `
	[ref] $result, 
	[ref] $errorFlag, `
	[int] $exceptionType `
)
{

	[string] $log = "";
	
	$errorFlag.value = $false;
	$result.value = $null;
	
         
	try 
	{ 
	
		 # Get Name of current function
		 $functionName = Get-FunctionName;
		 
		 Write-Output "";
	
	     if ($operator -eq "+" )
	     {
			 
			$result.value = $number1 + $number2;
			
	     }
	     elseif ($operator -eq "-" )
	     {
			$result.value = $number1 - $number2;
			
	     }	
	     elseif ($operator -eq "*" )
	     {
			 
			$result.value = $number1 * $number2;
			
	     }	
	     elseif ($operator -eq "/" )
	     {

			<#
				If we are dividing and the bottom number is  zero, preemtively raise an argument exception
			#>
			if ($number2 -eq 0)
			{
				
			   $errorFlag.value = $true;
			   
			   			   
			   $log = $FORMAT_MESSAGE_DIVISOR_CANNOT_BE_ZERO -f $moduleName, $functionName, $number2, $operator;
			   
			   if ($exceptionType -eq 1)
			   {
				
					throw (New-Object -TypeName System.ArgumentException -ArgumentList "$log");
					
			   }
			   elseif ($exceptionType -eq 2)
			   {
					
					throw (New-Object -TypeName System.ArgumentException -ArgumentList "$log", "number2" );
					
			   }	
			   else
			   {	   
				   			
				   Write-Warning $log;
				   
			   }			   
			    
	   
			}
			else 
			{
				$result.value = $number1 / $number2;
			}
				
		}
	   
	    elseif ($operator -eq "%" )
	    {
		
			$result.value = $number1 % $number2;
	    }
	 }
	 catch 
	 {
	 
		#Write-Output "An error occurred:";
		
		Write-Output $_;	
					
		
	 }
	 
	 Write-Output "";
	
	
}


<#
	Set Module Variables
#>
$moduleName = Get-ScriptFileName;

<#
	Division ( [/] ) 
#>

	$number1=6;

	$number2=3;
	
	$operator = "/";
	
	$logInternal = "";
	
	$errorFlag = $false;

	$result = calculator -operator $operator -number1 $number1 -number2 $number2 

	$log = $FORMAT_OUTPUT -f $number1, $operator, $number2, $result;

	Write-Output $log;
	Write-Output "";	
	

	# write dividing line
	writeDivider -ch $dividingChar -length $dividingLength	

	$number1=6;

	$number2=0;
	
	$operator = "/";

	<#
		If error handling is off, then walk in the dark
	#>
	if ($bErrorHandling -eq $false)
	{

		$result = calculator -operator $operator -number1 $number1 -number2 $number2

		$log = $FORMAT_OUTPUT -f $number1, $operator, $number2, $result;

		Write-Output $log;
		Write-Output "";
		
		# write dividing line
		writeDivider -ch $dividingChar -length $dividingLength

	}
	<#
		If error handling is on, then check your input arguments
	#>	
	else
	{

		$result = $null;
		$errorFlag = $false;
		
		
		$exceptionType = 1;
		
		calculatorGraceful `
			-operator $operator `
			-number1 $number1 `
			-number2 $number2 `
			-result ([ref]$result ) `
			-errorFlag ([ref]$errorFlag ) `
			-exceptionType $exceptionType			

		# write dividing line
		writeDivider -ch $dividingChar -length $dividingLength

		$exceptionType = 2;
		
		calculatorGraceful `
			-operator $operator `
			-number1 $number1 `
			-number2 $number2 `
			-result ([ref]$result ) `
			-errorFlag ([ref]$errorFlag ) `
			-exceptionType $exceptionType

		# write dividing line
		writeDivider -ch $dividingChar -length $dividingLength
		
		$exceptionType = 0;
		
		calculatorGraceful `
			-operator $operator `
			-number1 $number1 `
			-number2 $number2 `
			-result ([ref]$result ) `
			-errorFlag ([ref]$errorFlag ) `
			-exceptionType $exceptionType
			
		# write dividing line
		writeDivider -ch $dividingChar -length $dividingLength


		<#
			If we did not raise an error, then display answer
		#>
		if ($errorFlag -eq $false)
		{

			$log = $FORMAT_OUTPUT -f $number1, $operator, $number2, $result;

			Write-Output $log;
			Write-Output "";	
			
		}
		
	}

 

Output

Exception Type

Exception Type Is 1

Text

                In Module calculatorModularized.exceptionHandling.ps1, Function calculatorGraceful:- number2 (0) is invalid.  It can not be zero when operator is /
At C:\calculatorModularized.exceptionHandling.ps1:222 char:6
+ ...             throw (New-Object -TypeName System.ArgumentException -Arg ...
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (:) [], ArgumentException
    + FullyQualifiedErrorId :           In Module calculatorModularized.exceptionHandling.ps1, Fuction calculatorGraceful:- number2 (0) is invalid.  It can not be zero when operator is /

Image

Exception Type Is 2

Text

 number2 (0) is invalid. It can not be zero when operator is /
Parameter name: number2
calculatorModularized.exceptionHandling.ps1:142 char:6
+ ... throw (New-Object -TypeName System.ArgumentException -Arg ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], ArgumentException
+ FullyQualifiedErrorId : number2 (0) is invalid. It can not be zero when operator is /
Parameter name: number2

Image

Exception Type Is 0

Text

WARNING: number2 (0) is invalid. It can not be zero when operator is /

Image

Source Code

GitLab

  1. Repository
    • Folder
      Link
    • File
      calculatorModularized.exceptionHandling.ps1

 

Summary

Chances are you will experience unexpected data and user inputs.

It is up to you, whether you walk blindly into them and have the system handle them.

Or you watchfully sanitize your data a bit.

 

Referenced Work

  1. Microsoft
    • Learn / PowerShell / Scripting / Reference / Microsoft.PowerShell.Utility

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s