Private/Wissen/C_Advance/C04_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', '')]
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
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 1

#endregion

#region Meldungen u.a. unterdrücken

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

# ! Mögliche Werte sind:
[System.Management.Automation.ActionPreference]::Continue
[System.Management.Automation.ActionPreference]::Ignore
[System.Management.Automation.ActionPreference]::Inquire
[System.Management.Automation.ActionPreference]::SilentlyContinue
[System.Management.Automation.ActionPreference]::Stop
[System.Management.Automation.ActionPreference]::Suspend

# ? Globale Unterdrückung:
$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop # ! Default => Continue TIPP: In .PS1-Dateien im Kopfbereich auf Stop setzen!
$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

# ? Beispiel A:
1/0

$Error[0] # Ist immer der letzte Fehler
$Error[0] | Get-ErrorInfo # ! ACHTUNG nur im AKPT-Modul oder PowerShell 7 mit Get-Error s.o.

# ? Beispiel B:
$myErrors = @()
Get-Process -FileVersionInfo -ErrorVariable 'myErrors'
"Aufgetretene Fehler: $($myErrors.Count)"
$myErrors[0] | Get-ErrorInfo # ! ACHTUNG nur im AKPT-Modul oder PowerShell 7 mit Get-Error s.o.

# ? Beispiel C:
$myErrors = @()
Get-Content -Path 'C:\MichGibtEsNicht.txt' -ErrorVariable 'myErrors'
"Aufgetretene Fehler: $($myErrors.Count)"
$myErrors | Get-ErrorInfo # ! ACHTUNG nur im AKPT-Modul oder PowerShell 7 mit Get-Error s.o.

#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 Spooler -Force
if ($?) {
    "'Stop-Service -Name Spooler -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
    $_.Message
    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'
    break
}

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

$ErrorActionPreference = $ErrorActionPreferenceBackup

#endregion

#endregion

#region Eigene Fehler auslösen

# ? Native PowerShell Fehler (RuntimeException)
1/0

# ? ... durch Typenfestlegung
[byte]$Hubraum = 5000
[System.Net.Mail.MailAddress]$ToEmail = 'Attila Krick<info@attilakrick.com>'

# ? ... durch Write-Error
'UPS, da hat etwas nicht geklappt (ErrorCode: 4711)' | Write-Error

# ? ... durch div. Validate-Attributen für eigen Cmdlet-Parameter
[ValidateRange(5, 10)]$Hubraum = 6
$Hubraum = 11
[ValidatePattern('^[a-z]{2,5}[0-9]+')]$HostName = 'abc2'
$HostName = 'ab1'

# ? ... 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
        $this.KundeId = $kundeId
    }
}
throw New-Object -TypeName KundeVorhandenException -ArgumentList "Der Kunde ist bereits vorhanden", 4711

#endregion

#region Übungen

# TIPPS: Get-ErrorDetails; $error

# 1. Welche Werte kann die `$ErrorActionPreference`-Variable aufnehmen, welche Bedeutung haben diese Werte und wie sind Sie auf diese Werte/Informationen gekommen?
# 2. Welche Exception werden von den folgenden Code-Zeilen ausgelöst? Get-Process -Name Haeckihaeckipateng
# 3. Welche Exception werden von den folgenden Code-Zeilen ausgelöst? 100 / $xyz
# 4. Welche Exception wird von folgender Code-Zeile ausgelöst? [datetime]'31.12.2019'
# 5. Welche Exception werden von den folgenden Code-Zeilen ausgelöst? Invoke-WebRequest -Uri 'http:\\www.gfu.net\Seminare.html'

#endregion