Private/Wissen/C_Advance/C05_Error.ps1

<#
 
# Error
 
Fehler/Exception unterdrücken, analysieren, behandeln oder auslösen
 
- **Hashtags** ErrorRecord Exception $error trap try catch finally
- **Version** 2020.01.23
 
#>


[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingEmptyCatchBlock', '')]
param()

# TODO Weiterführende und Nachschlage-Informationen
Get-Help -Name about_Try_Catch_Finally -ShowWindow
Get-Help -Name about_Trap              -ShowWindow
Get-Help -Name about_Throw             -ShowWindow
# * Siehe Thema .\AKPT\Private\Wissen\C_Scripting\C04_Debugging.ps1

#region Neuerungen der PowerShell 7

# ! Standard-Fehlerausgabe-Menge über $ErrorView bestimmen
Get-help -Name ABOUT_PREFERENCE_VARIABLES -ShowWindow

$ErrorView = [System.Management.Automation.ErrorView]::NormalView # ! Umfassende Fehler-Ausgabe
1/0 # * Fehler provozieren!
$ErrorView = [System.Management.Automation.ErrorView]::ConciseView # ! DEFAULT: Fehler-Ausgabe enthält nur eine Kurzfassung
1/0 # * Fehler provozieren!
$ErrorView = [System.Management.Automation.ErrorView]::CategoryView # ! Nur Fehler-Ausgabe enthält nur die Kategorie
1/0 # * Fehler provozieren!

# ! Wird der ErrorAction der neue Wert 'Break' zugewiesen, hält die Ausführung an und es wird den DEBUG-Modus gewechselt
$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Break
Get-Process -FileVersionInfo -ErrorAction Break

# ! Die letzten Fehler auswerten mit dem neuen Cmdlet 'Get-Error':
Get-Process -FileVersionInfo # * Fehler provozieren!

Get-Error
Get-Error -Newest 2

#endregion

#region Meldungen u.a. unterdrücken

# ? Lokal Unterdrückung:
Get-Process -FileVersionInfo -ErrorAction Stop -WarningAction Ignore -InformationAction Continue -Verbose

# ? Globale Unterdrückung:
$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop # ! Default => Continue
$WarningPreference     = [System.Management.Automation.ActionPreference]::Stop # ! Default => Continue
$InformationPreference = [System.Management.Automation.ActionPreference]::Stop # ! Default => SilentlyContinue
$VerbosePreference     = [System.Management.Automation.ActionPreference]::Stop # ! Default => SilentlyContinue

Get-Process

#region Unterdrückung in eigenen Cmdlets einbauen

function Test-CommonParameter {
    [CmdletBinding()] # ! WICHTIG um die Common-Parameters nutzen zu können
    param (

    )
    "Error Test"       | Write-Error
    "Warning Test"     | Write-Warning
    "Verbose test"     | Write-Verbose
    Write-Information -MessageData "Information Test"
    "Debug Test"       | Write-Debug
}
Test-CommonParameter
Test-CommonParameter -ErrorAction SilentlyContinue -WarningAction Ignore -Verbose -InformationAction Continue
Test-CommonParameter -Debug

#endregion

#endregion

#region Fehler analysieren

# ! Hilfreiches Cmdlet zur Analyse von Fehler:
filter Get-ErrorDetails {
    $Category         = @{ Name = 'CategoryInfo'          ; Expression = { $_.CategoryInfo.Category           }}
    $Line             = @{ Name = 'LineNumber'            ; Expression = { $_.InvocationInfo.ScriptLineNumber }}
    $Script           = @{ Name = 'ScriptName'            ; Expression = { $_.InvocationInfo.ScriptName       }}
    $Target           = @{ Name = 'Target'                ; Expression = { $_.TargetObject                    }}
    $ErrorId          = @{ Name = 'ErrorId'               ; Expression = { $_.FullyQualifiedErrorID           }}
    $ExType           = @{ Name = 'ExceptionType'         ; Expression = { $_.Exception.GetType().FullName    }}
    $ExMessage        = @{ Name = 'ExceptionMessage'      ; Expression = { $_.Exception.Message               }}
    $ExInnerException = @{ Name = 'InnerExceptionMessage' ; Expression = { $_.Exception.InnerException        }}

    $Input | Select-Object -Property $Category, $Line, $Script, $Target, $ErrorId, $ExType, $ExMessage, $ExInnerException
}

# ? Beispiel A:
$myErrors = @()
Get-Process -FileVersionInfo -ErrorVariable myErrors
"Aufgetretene Fehler: $($myErrors.Count)"
$myErrors | Get-ErrorDetails

# ? Beispiel B:
$myErrors = @()
Get-Content -Path C:\MichGibtEsNicht.txt -ErrorVariable myErrors
"Aufgetretene Fehler: $($myErrors.Count)"
$myErrors | Get-ErrorDetails

#region Die $error-Systemvariable

$error # Enthält 256 Fehler seit beginn der Session
$MaximumErrorCount
$MaximumErrorCount = 999 # Default 256
$error.Count
$error | Select-Object -Last 1  # ! Der älteste Fehler ($error[$error.Count - 1])
$error | Select-Object -First 1 # ! Der neuste Fehler ($error[0])

# ? Nutzen, z.B.:
$error.Clear()
42 / 0
"Aufgetretene Fehler: $($error.Count)"
$error | Get-ErrorDetails

#endregion

# Wurde der letzte Befehl erfolgreich ausgeführt?
Stop-Service -Name Audiosrv -Force
if ($?) {
    "'Stop-Service -Name Audiosrv -Force' wurde erfolgreich ausgeführt!"
}

#endregion

#region Fehler behandeln

#region per try, catch und finally

# ! try..catch nur benutzen wenn EINE LÖSUNG implementiert wird,
# ! da dieses Konstrukt der PowerShell signalisiert das der Fehler behoben wurde!

# ! #######################################
# ! ### EINE MELDUNG IST KEINE LÖSUNGEN ###
# ! #######################################

try {
    $ErrorActionPreferenceBackup = $ErrorActionPreference
    $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
    # ! Code der evtl. einen Fehler auslöst:
    Get-Content c:\temp\wichtig.txt
}
catch [System.Management.Automation.ItemNotFoundException] {
    # ! Code der diesen Fehler behebt:
    New-Item c:\temp\wichtig.txt
}
catch [System.OutOfMemoryException] { 
    # ! Völlig spezifischer Fehler
    # ! Code der diesen Fehler behebt
}
catch [System.SystemException] { # Bereichsfehler

}
catch { # Völlig unspezifische Fehler
    # ! Code der diesen Fehler behebt => Was könnte das nur sein !?!?!?
    $_.GetType().FullName
    throw $_
}
finally {
    $ErrorActionPreference = $ErrorActionPreferenceBackup
}

#endregion

#region über trap

$ErrorActionPreferenceBackup = $ErrorActionPreference
$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop

trap [System.DivideByZeroException] {
    '[SIRENE] => Division durch 0 wurde festgestellt'
    continue
}

trap [System.Management.Automation.ActionPreferenceStopException] { 
    '[SIRENE] => Eine ActionPreferenceStopException ist aufgetreten, tatsächliche Exception auslösen und abfangen'
    throw $_.Exception
    continue

    trap [System.Management.Automation.ItemNotFoundException] {
        '[SIRENE] => Die Datei wurde nicht gefunden'
        continue
    }

    trap  {
        '[SIRENE] => Irgend ein anderer Fehler in ActionPreferenceStopException ist aufgetreten'
        continue
    }
}

trap  {
    '[SIRENE] => Irgend ein anderer Fehler ist aufgetreten'
    continue
}

12/0
Get-Content -Path C:\MichGibtEsNicht.txt
Get-Process -Name wininit -FileVersionInfo

$ErrorActionPreference = $ErrorActionPreferenceBackup

#endregion

#endregion

#region Eigene Fehler auslösen

# ? ... durch Typenfestlegung
[byte]$Hubraum = 5000

# ? ... durch Write-Error
"UPS, da hat etwas nicht geklappt" | Write-Error

# ? ... durch div. Validate-Attributen für eigen Cmdlet-Parameter
[ValidateRange(5, 10)]$Hubraum = 6
$Hubraum = 11

# ? ... durch throw String-Text
throw "Ihr Geburtsdatum muss kleiner oder gleich Heute $(Get-Date -Format dd.MM.yyyy) sein."

# ? ... durch das Auslösen einer vorhanden Exception-Klasse
throw New-Object -TypeName "System.ArgumentException" -ArgumentList "Wert 'xyz' ist ungültig.", "Geburtsdatum"

# ? ... durch das Auslösen einer selbst deklarierte Exception-Klasse
[System.SerializableAttribute()]
class KundeVorhandenException : System.Exception
{
    [string]$Message
    [int]$KundeId
    
    KundeVorhandenException([string]$message, [int]$kundeId) : base("$message (Kunde $kundeId)") {
        $this.Message = $message
    }
}
throw New-Object -TypeName KundeVorhandenException -ArgumentList "Der Kunde ist bereits vorhanden", 4711

#endregion

#region Übungen

<# TODO ÜBUNG Error Handling 1
    ! VERTIEFUNG: Exception-Namen ermitteln
    ? Welche Exception werden von den folgenden Code-Zeilen ausgelöst?
 
        Invoke-WebRequest -Uri "http:\\www.gfu.net\Seminare.html"
        100 / $xyz
        Get-Process -Name Haeckihaeckipateng
        [datetime]"31.12.2019"
    
    TIPPS: Get-ErrorDetails; Resolve-ErrorRecord; $error
    * LÖSUNG
#>


#endregion