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
- Function:- calculatorGraceful
- If operation is equal and divisor is 0
- If exceptionType is 1
- throw (New-Object -TypeName System.ArgumentException –ArgumentList “$log”);
- If exceptionType is 2
- throw (New-Object -TypeName System.ArgumentException –ArgumentList “$log”, “number2” );
- Else
- Write-Warning
- If exceptionType is 1
- If operation is equal and divisor is 0
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
- Repository
- Folder
Link - File
calculatorModularized.exceptionHandling.ps1
- Folder
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.