Wissen/B05_Ausgabe.ps1

# ? TITEL Ausgabe
# ? DESCRIPTION Ergebnisse ausgeben
# ? TAGS Format Write Out Convert
# ? VERSION 2019.11.10

#region Format-*

#! Bereitet die Objekte für eine Endausgabe auf.
#! I.d.R. ist danach ein weiteres Bearbeiten von Objekten nicht mehr möglich!

# TODO Weiterführende und Nachschlage-Informationen
Get-Command -Verb Format -Module Microsoft.PowerShell.*

# ?
# ? Format-Table (ft)
# ?

# Eine Default-Ausgabe gem. *.ps1xml-Dateien
Get-Process

# Ausgewählte Spalten
Get-Process | Format-Table -Property Name, Company, CPU, TotalProcessorTime, WorkingSet64 

# Breite optimal anpassen
Get-Process | Format-Table -Property Name, Company, CPU, TotalProcessorTime, WorkingSet64 -AutoSize

# Benutzerdefinierte Spalten hinzufügen @{ Label="" ; Expression={$_} }
Get-Process | Format-Table -Property Name, @{Label="WS (MB)"; Expression={[int](          $_.WorkingSet64 / 1MB)   }}
Get-Process | Format-Table -Property Name, @{Label="WS (MB)"; Expression={[Math]::Round(  $_.WorkingSet64 / 1MB, 1)}}
Get-Process | Format-Table -Property Name, @{Label="WS (MB)"; Expression={"{0,7:0.0}" -f ($_.WorkingSet64 / 1MB)   }}

# Inhalt der auf ... endet in weitere Zeilen umbrechen
Get-EventLog -LogName System -Newest 10 -EntryType Error | Format-Table -Wrap

# ?
# ? Format-List (fl)
# ?

Get-Process | Format-List -Property Name, Company, CPU # Spezielle Spalten
Get-Process | Format-List -Property *

# ?
# ? Format-Wide (fw)
# ?

Get-Alias | Sort-Object -Property Name | Format-Wide -Property Name -Column 10

#endregion

#region Write-*

# TODO Weiterführende und Nachschlage-Informationen
Get-Command -Verb Write -Module Microsoft.PowerShell.Utility

# ! ACHTUNG: Write-Host nicht in Kombination mit PowerShell Core benutzen (Write-Host ist veraltet! Nachfolger Write-Output)

"Hallo Köln!" | Write-Output # Das Ziel kann angegeben werden z.B. Datei, etc.
"Achtung ich bin ein Fehler" | Write-Error  # Stop das Script wenn '-ErrorAction Stop' gesetzt
Write-Warning "und ich bin eine Warnung" # Evtl. wird das Script gestoppt -WarningAction ..............
Write-Debug "und ich eine Debug-Meldung" # -Debug

# ! Die Anzeige von 'Write-Progress' wird z.Zt. in VSCode NICHT anzeigt!
0..100 | ForEach-Object -Process {
    Write-Progress -Activity "Verarbeite Daten" -Status "$_ Prozent" -PercentComplete $_
    Start-Sleep -Milliseconds 100 # Test-Last
}

# bzgl. Write-Error und Kollegen
Get-ChildItem -Path HKLM:\ -Recurse -Force -ErrorAction Stop
$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop # ! In Skripte im Header setzen
Get-ChildItem -Path hklm:\ -Recurse -Force

#endregion

#region OUT-* (Umleiten der Konsolen-Ausgabe)

# TODO Weiterführende und Nachschlage-Informationen
Get-Command -Verb Out -Module Microsoft.PowerShell.*

Get-ChildItem -Path c:\ -Recurse | more # ! Unbrauchbar
Get-ChildItem -Path c:\ -Recurse | Out-Host -Paging # Die Seiten werden sofort angezeigt

#endregion

#region Convert..-

# TODO Weiterführende und Nachschlage-Informationen
Get-Command -Verb ConvertTo, ConvertFrom -Module Microsoft.PowerShell.*

Get-Process | ConvertTo-Csv | out-File C:\temp\procs.csv
Get-Process | Select-Object Name, Company, WorkingSet64 | ConvertTo-Csv -Delimiter ";" -NoTypeInformation | Set-Content C:\temp\procs.csv
. C:\temp\procs.csv

Get-Process | Select-Object Name, Company, WorkingSet64 | ConvertTo-Html | Out-File C:\temp\procs.html
. C:\temp\procs.html

#$pwd = "P@ssw0rd" # ! Nicht sicher!
#$pwd = ConvertTo-SecureString -String P@ssw0rd -AsPlainText -Force # ! Auch nicht sicher!
$pwd = Read-Host -Prompt "Passwort eingaben" -AsSecureString       # ! Sicher!

#endregion

# * Siehe auch: Send-MailMessage ; Set-Content ; Invoke-SqlCmd -Query "INSERT INTO ..."

# ! Einfache GUI sind mit der PS problemlos mit geringen Aufwand möglich.
# ! Bei komplexen GUI stößt die PS an Ihre Grenzen da der Aufwand unverhältnismäßig ist.
# !FAUSTSFORMEL: Eingabe- und Ausgabe-GUI = OK
# TODO Cmdlet-Grundgerüst per GUI bauen => https://poshgui.com/CmdletBuilder

#region eine ALARM-Meldung

Add-Type -AssemblyName PresentationFramework
Add-Type -AssemblyName System.Speech
$xaml = @"
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp2"
        WindowStartupLocation="CenterScreen"
        ResizeMode="NoResize"
        WindowStyle="None"
        mc:Ignorable="d"
        SizeToContent="WidthAndHeight"
        Title="MainWindow">
    <Grid Background="Red">
        <TextBlock FontFamily="Consolas" FontSize="100" FontWeight="ExtraBold" Foreground="White" Margin="50" HorizontalAlignment="Center" VerticalAlignment="Center">A L A R M !</TextBlock>
    </Grid>
</Window>
"@

$reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]$xaml)
$window = [System.Windows.Markup.XamlReader]::Load($reader)
$window.Topmost = $true
$window.Show() | Out-Null
$speaker = New-Object -TypeName System.Speech.Synthesis.SpeechSynthesizer -Property @{ Rate = 2; Volume = 100 }
$speaker.SpeakAsync('ALARM! ALARM! ALARM! ALARM!')
Start-Sleep -Seconds 5
$window.Close()

#endregion

#region .NET Framework WPF 'Hallo Welt'

function Invoke-WpfHalloWelt {

    [CmdletBinding(SupportsShouldProcess=$false)]
    param()

    Add-Type -AssemblyName PresentationFramework
    Add-Type -AssemblyName System

    [XML]$XAML = @"
    <Window x:Class="FSM.PS_WPF_Sample"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="PS_WPF_Sample" Height="200" Width="300">
        <Grid>
            <TextBox x:Name="PSText" Text="Hello World" HorizontalAlignment="Left" Margin="65,40,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="175"/>
            <Label x:Name="PSLabel" Content="Type in the box and click OK" HorizontalAlignment="Left" Margin="65,80,0,0" VerticalAlignment="Top" Width="175" BorderThickness="1"/>
            <Button x:Name="PSBtnOK" Content="OK" HorizontalAlignment="Left" Margin="65,130,0,0" VerticalAlignment="Top" Width="75"/>
            <Button x:Name="PSBtnExit" Content="Exit" HorizontalAlignment="Left" Margin="165,130,0,0" VerticalAlignment="Top" Width="75"/>
        </Grid>
    </Window>
"@


    $XAML.Window.RemoveAttribute("x:Class")
    $Reader = New-Object System.Xml.XmlNodeReader $XAML
    $Form = [Windows.Markup.XamlReader]::Load($Reader)

    $PSText    = $Form.FindName('PSText')
    $PSLabel   = $Form.FindName('PSLabel')
    $PSBtnOK   = $Form.FindName('PSBtnOK')
    $PSBtnExit = $Form.FindName('PSBtnExit')

    $btnOKClick   = {$PSLabel.Content = $PSText.Text}
    $btnExitClick = {$Form.Close()}

    $PSBtnOK.Add_Click($btnOKClick)
    $PSBtnExit.Add_Click($btnExitClick)

    $Form.ShowDialog()
}

Invoke-WpfHalloWelt

#endregion

#region Diagramm erstellen per DataVisualization

using namespace System.Windows.Forms.DataVisualization.Charting
using namespace System.Windows.Forms
using namespace System.Drawing

Add-Type -AssemblyName System.Windows.Forms.DataVisualization

$chartTitle = "Top Memory Usage"
$axisYTitle = "Memory (MB)"
$axisXTitle = "Process Name"
$dataSource = Get-Process | 
    Sort-Object PrivateMemorySize -Descending  | 
    Select-Object -First 10 | ForEach-Object -Process {
        [PSCustomObject]@{
            Name              = $_.Name
            PrivateMemorySizeMB = [int]($_.PrivateMemorySize / 1MB)
            VirtualMemorySizeMB = [int]($_.VirtualMemorySize / 1MB)
        }
    }

$chart1 = New-Object Chart
$chart1.Width = 1200
$chart1.Height = 1000
$chart1.BackColor = [Color]::White
$chart1.Dock = [DockStyle]::Fill

$chart1.Titles.Add($chartTitle) | Out-Null
$chart1.Titles[0].Font = "Arial,13pt"
$chart1.Titles[0].Alignment = [ContentAlignment]::TopCenter

$chartArea = New-Object ChartArea
$chartArea.Name = "ChartArea1"
$chartArea.AxisY.Title = $axisYTitle
$chartArea.AxisX.Title = $axisXTitle
$chartArea.AxisY.Interval = 100
$chartArea.AxisX.Interval = 1
$chart1.ChartAreas.Add($chartArea)

$legend = New-Object Legend
$legend.name = "Legend1"
$chart1.Legends.Add($legend)

$chart1.Series.Add("VirtualMem") | Out-Null
$chart1.Series["VirtualMem"].ChartType = "Column"
$chart1.Series["VirtualMem"].BorderWidth  = 3
$chart1.Series["VirtualMem"].IsVisibleInLegend = $true
$chart1.Series["VirtualMem"].ChartArea = "ChartArea1"
$chart1.Series["VirtualMem"].Legend = "Legend1"
$chart1.Series["VirtualMem"].color = [Color]::LightGreen
$dataSource | ForEach-Object {
    $chart1.Series["VirtualMem"].Points.AddXY( $_.Name, $_.VirtualMemorySizeMB)  | Out-Null
}

$chart1.Series.Add("PrivateMem") | Out-Null
$chart1.Series["PrivateMem"].ChartType = "Column"
$chart1.Series["PrivateMem"].IsVisibleInLegend = $true
$chart1.Series["PrivateMem"].BorderWidth  = 3
$chart1.Series["PrivateMem"].ChartArea = "ChartArea1"
$chart1.Series["PrivateMem"].Legend = "Legend1"
$chart1.Series["PrivateMem"].color = [Color]::Orange
$dataSource | ForEach-Object {
    $chart1.Series["PrivateMem"].Points.AddXY( $_.Name, $_.PrivateMemorySizeMB) | Out-Null
}
            
# z.B. als PNG speichern, oder
$chart1.SaveImage("c:\temp\$chartTitle.png", "png")

# z.B. im Fenster anzeigen
$Form = New-Object Form
$Form.Width = 1300
$Form.Height = 800
$Form.StartPosition = [FormStartPosition]::CenterScreen
$Form.controls.add($chart1)
$Form.Add_Shown({$Form.Activate()})
$Form.ShowDialog()

#endregion