Kategorieauswahl

Script

PowerShell Script um Quest-Migration-Manager Datenbank aufzuräumen

Das folgende Script löscht Einträge aus der Ressource Processing Datenbank des Quest Migration Manager, die älter als 3 Monate sind:

# Bitte unter dem Namen cleanQuestDB.ps1 abspeichern, damit das u.a. Beispiel passt
 param(
 [String]$Server=“localhost“,
 [int]$Port=50000,
 [int]$Month=3,
 [String]$LogPath=“~\Desktop\DeletedQuestDBEntries.log“
)
Import-Module ActiveDirectory
$ErrorActionPreference=“SilentlyContinue“
Get-ADObject -filter * -server „$($Server):$($Port)“ -searchbase *
$Root=($Error[0] | select -expandprop Exception | select -expandprop message).split(„:“)[1].split(„,“)[-1].substring(1).trimend(„‚.“)
$ErrorActionPreference=“Continue“
$Project=(Get-ADObject -filter * -server „$($Server):$($Port)“ -searchbase $Root -searchscope Onelevel | ? {$_.ObjectClass -eq „aelita-Amm-Workspace“}).Distinguishedname
$ComputerLogCollection=Get-ADObject -filter * -server „$($Server):$($Port)“ -searchbase „cn=Computers,cn=ResourceProcessing,$Project“ -searchscope onelevel -properties aelita-Amm-LastOperationTime,aelita-Amm-Name
$TimeSpan=(get-date)-(new-timespan -days ($Month*30))
$ToDelete=@()
$TotalCounter=$ComputerLogCollection.count
$DeleteCounter=0
„Searching Database. Please be patient!“
foreach ($Item in $ComputerLogCollection) {
 $Timestamp=$Item | select -expandprop aelita-Amm-LastOperationTime
 if ($Timestamp -and $Timestamp -lt $TimeSpan) {
  $ToDelete+=$Item  
  $DeleteCounter++
 }
}
if ((read-host -prompt „$DeleteCounter from $TotalCounter to delete, proceed (Yes/No)“) -notlike „Y*“) {„Aborted!“;exit}
„Deleted Entries of Questdatabase“ > $LogPath
„Run from: $(get-date)“ >> $LogPath
foreach ($Item in $ToDelete) {
 $Name=$Item | select -expandprop aelita-Amm-Name
 $Timestamp=$Item | select -expandprop aelita-Amm-LastOperationTime
 „Deleting $Name from $Timestamp“ | tee $LogPath -Append
 $Item | Remove-ADObject -Recursive -confirm:$false
}

Im einfachsten Fall starten Sie einfach das Script. Zunächst wird geschaut wieviele Einträge insgesamt vorhanden sind und wieviele davon gelöscht werden würden. Dann fragt es, ob Sie den Vorgang durchführen möchten und listet Ihnen die entsprechenden Objekte auf. Nebenbei werden diese Angaben in eine LogDatei auf Ihrem Desktop protokolliert. Sie können das Skript natürlich auch mit anderen Parametern aus dem Parameter Block aufrufen, wie z.B.:

./cleanQuestDB.ps1 -Server QuestServer -Port 40000 -Month 2 -LogPath C:\Quest.log

Voraussetzungen:
PowerShell 2.0 oder höher
AD-Modul verfügbar
Der Benutzer unter dem das Script läuft benötigt Vollzugriff auf die Quest-Datenbank
Muss mit erhöhten Rechten (UAC – Benutzerkontensteuerung) ausgeführt werden

Fehler in der Beschreibung der PowerShell von get-help about_Advanced-Funktionen

In PowerShell kann man übergebene Parameter durch die PowerShell selbst überprüfen lassen. Das ist cool! Leider hat sich in der Beschreibung zur Überprüfung von Strings (Texten) ein Fehler in about_Advanced-Funktionen eingeschlichen. Im about File steht:

Param
    (
       [parameter(Mandatory=$true)]
       [String[]]
       [ValidateRange ("Sven", "Monica", "Christian")] 
       $UserName
    )
$UserName

Richtig ist:

Param (
            [parameter(Mandatory=$true)]
            [String[]]
            [ValidateSet("Sven", "Monica", "Christian")] 
            $UserName
)
$UserName

Achtung!!! Nicht nur das Wort Range ist durch Set auszutauschen, sondern nach dem Wort Set darf auch kein Leerzeichen zu der nachfolgenden ( stehen. Dann funktioniert es auch mit dem Aufruf. Speichern Sie den Textschnipsel z. B. direkt als test.ps1 ab. Wenn Sie dann das Skript mit Parameter starten, gibt’s bei der ersten Variante die gewünschte Fehlermeldung der Shell und in der zweiten ist der übergebene Parameter ja aufgelistet und er wird entsprechend ausgegeben.

.\test.ps1 blabla
.\test.ps1 Sven

Zahlenformate in Powershell konvertieren

Um Zahlenformate in Powershell zu konvertieren kann man die Funktionen ToString der Klasse System.Convert von .NET verwenden. Das geht recht einfach:

$a=16 # Standardmäßig ist das erst einmal eine Dezimalzahl, also zu Basis 10
[System.Convert]::tostring($a,2) # Basis 2 macht daraus eine Binärzahl
[System.Convert]::tostring($a,8) # Basis 8 macht daraus eine Oktalzahl
[System.Convert]::tostring($a,16) # Basis 16 macht daraus eine Hexadezimalzahl

Mit Powershell aus dem AD Objekte abholen und nach nicht leeren Eigenschaften filtern

Es gibt viele Scriptbeispiele um mit Powershell Objekte aus dem Active-Directory abzuholen, bei denen eine bestimmte Eigenschaft ausgefüllt ist, oder nicht. Allerdings habe ich noch kein Script gefunden, dass ein Objekt abholt und dann davon alle leeren oder ausgefüllten Felder anzeigt. Das Problem liegt im Rückgabe-Wert des AD-Objekts. Dieses ist nämlich als Hash aufgehängt und läßt somit einen einfachen Zugriff auf den Wert zu. Erst mit Hilfe der .NET-Methode GetEnumerator() ist es möglich den Inhalt auszuwerten wie das folgende Beispiel anhand von get-aduser zeigt:

(get-aduser Administrator -properties *).getenumerator() | ? {$_.value} # zeigt alle ausgefüllten Eigenschaften
(get-aduser Administrator -properties *).getenumerator() | ? {!$_.value} # zeigt alle nicht ausgefüllten Eigenschaften

Powershell Variablen mit Befehlen belegen

Sie können in Powershell ganze Befehlsketten in Variablen ablegen, z. B. so:

$a=get-childitem

Dann wird allerdings zum Zeitpunkt der Variablendeklaration der Befehl ausgeführt und der Inhalt in der Variablen hinterlegt. Legt man also nun ein neues Verzeichnis an, ist dieses nicht in $a enthalten, da die Variable zuerst in Inhalt übergeben bekam und erst danach der neue Ordner erstellt wurde. Gibt man nun $a aus, fehlt der neu erstellte Ordner. Man kann allerdings auch Code in einer Variablen zum Zeitpunkt der Variablenabfrage ausführen lassen. Dazu müssen Sie den Code nur in geschweifte Klammern setzen und die Variable mit vorangestelltem Punkt abrufen. Also beispielsweise so:

$a={get-childitem}

Wenn Sie nun:

. $a

abrufen, wird der Inhalt der Variablen zum Zeitpunkt von der Angabe . $a als Code ausgeführt und enthält somit den aktuellen Stand des Verzeichnisses.

Auch interessant ist die Möglichkeit Befehle mit Variablen zu Befehlen zusammen zuschrauben.

$Verb="set"
&("$Verb-service")

Powershellscripte zur Verwaltung von Hyper-V

Wenn Sie kein SCVMM (System Center Virtual Machine Manager) Ihr eigen nennen wäre es doch trotzdem ganz nett einige Dinge davon selbst über Powershell zu machen. Microsoft hat eine Powershell Script Sammlung zur Hyper-V Verwaltung bereit gestellt. Damit können Sie u. a. virtuelle Maschinen und Snapshots bzw. Checkpoints erstellen und verwalten aber auch vhd’s konvertieren und noch vieles mehr. Die Sammlung wir stetig erweitert. Mit der Anleitung Powershell GUI-Programmierung für Dummies auf dieser Seite könnten Sie sich Ihren eigenen SCVMM zusammen bauen. Wenn ich einmal viel Langeweile habe mache ich das vielleicht für Sie ;-).

Datenbankzugriffe mit der Powershell

Leider bringt Powershell keine einfachen Cmdlets für den Zugriff auf Datenbanken mit. Die einzige Möglichkeit ist über ADO.NET zuzugreifen.

Vorbereitung

Je nach installierten Datenbankprovider gibt es verschiedene Möglichkeiten die unterschiedlichen Datenbanken (Access,MS-SQL,MY-SQL,Oracle,ODBC, etc…) zuzugreifen. Welche Provider überhaupt auf dem Computer registriert (z.B. Access installiert) sind können Sie mit:

[System.Data.Common.DBProviderFactories]::GetFactoryClasses() | select InvariantName

heraus finden. Falls Sie auf einen Microsoft SQL Server zugreifen möchten können Sie diese mittels:

[System.Data.Sql.SqlDataSourceEnumerator]::Instance.GetDataSources()

auflisten lassen.

Access Datenbank anlegen bzw. erstellen

Eine neue Datenbankdatei in Access legt man wie folgt an:

$application = New-Object -ComObject Access.Application
$application.NewCurrentDataBase("C:\Pfad\Datei.mdb",10)
$application.CloseCurrentDataBase()
$application.Quit()

Tabelle in Access Datenbank anlegen bzw. erstellen

$connection = New-Object -ComObject ADODB.Connection
$connection.Open("Provider= Microsoft.Jet.OLEDB.4.0;Data Source=C:\Pfad\Datei.mdb" )
$Fields = "NamederSpalte1 Counter, NamederSpalte2 Date, NamederSpalte3 Integer, NamederSpalte4 Text"
$command = "Create Table GewünschterNamederTabelle `($Fields`)"
$connection.Execute($command)
$connection.Close()

Die fett gedruckten Passagen geben wieder Ihre persönlichen Anpassungen an.

1. Den Pfad zur Access Datenbankdatei

2. Die Spalten bzw. Felder die angelegt werden sollen. Erst den Namen der jeweiligen Spalte und danach gefolgt der Datentyp (Counter=Zähler, Date=Datum, Integer=Zahl, Text=Text) für die Spalte.

3. Name der Tabelle

Verbindungsaufbau und Tabelle auswählen

So stellen Sie eine Verbindung mit einer Datenbank her und öffnen eine Tabelle:

# Angaben zur Verbindung mit der Datenbank
$Verbindung="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\Pfad zu Ihrer Datenbank\Datei.mdb;"
$SQL="Select * from tabelle order by Vorname"
# Tabelle aus der Datenbank in Variable ablegen
$DB=New-Object System.Data.OleDB.OleDbConnection($Verbindung)
# Öffnen der Tabelle
$DB.open()
"Tabelle: "+$DB.State
# Schließen der Tabelle
$DB.close()
"Tabelle: "+$DB.State
Dies ist ein Beispiel für den Zugriff auf eine Access-Datenbank. Die unterstrichenen Abschnitte müssen Sie entsprechend anpassen. Das order by Vorname können Sie auch komplett weg lassen. Dies dient lediglich zur Sortierung nach der Spalte Vorname. Möchten Sie eine statische Verbindung zu einer MS-SQL Datenbank herstellen, müssen die Fett dargestellten Bereiche wie folgt abgeändert werden:

$Verbindung=“Data Source=.\RelativePositionderDatenbankbzwderenName;Initialcatalog=Users;Integrated Security=True;“

$DB=New-Object System.Data.SqlClient.SqlConnection($Verbindung)

Möchten Sie eine dynamische Verbindung zu einer MS-SQL Datenbank herstellen, müssen die Fett dargestellten Bereiche wie folgt abgeändert werden:

$Verbindung="Data Source=.\RelativePositionderDatenbankbzwderenName;AttachDbFileName=C:\Pfad\Datenbank.mdf;Integrated Security=True;"
$DB=New-Object System.Data.SqlClient.SqlConnection($Verbindung)     # wie bei der statischen Verbindung
Daten aus einer Tabelle auslesen

Das folgende Script zeigt die Möglichkeit Daten aus der Datenbank auszulesen. Die fett markierten Stellen sind die dokumentierten Änderungen zum vorangegangenen Script.

# Angaben zur Verbindung mit der Datenbank
$Verbindung="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\db\test.mdb;"
$SQL="Select * from Services order by Zeit"
# Tabelle aus Datenbank in Variable ablegen
$DB=New-Object System.Data.OleDb.OleDbConnection($Verbindung)
# Öffnen der Tabelle aus der Datenbank
$DB.open()
"Tabelle: "+$DB.State
$Kommando=New-Object System.Data.OleDb.OleDbCommand($SQL,$Verbindung) # Erstellt ein Object für das SQL-Kommando
$Datenadapter=New-Object System.Data.OleDb.OleDbDataAdapter($Kommando)    # Wendet das SQL-Kommando auf die Datenbank an
$Dataset=New-Object System.Data.Dataset            # Erstellt eine Object um die Daten aufzunehmen
[Void] $Datenadapter.Fill($Dataset,"Services")   # Holt sich die Daten ab
# Schließen der Datenbankverbindung
$DB.close()
"Tabelle: "+$DB.State
$Dataset.Tables[0] | ft          # Gibt die Daten als Tabelle formatiert auf dem Bildschirm aus
Daten in eine Tabelle schreiben
$strDB="C:\Pfad\Datei.mdb"
$strTable="Tabellenname"
$objCon=New-Object -comobject ADODB.Connection
$objRecordSet=New-Object -comobject ADODB.Recordset
$objCon.Open("Provider = Microsoft.Jet.OLEDB.4.0; Data Source = $strDB")
$objRecordSet.Open("SELECT * FROM Services",$objCon)
$objRecordSet.Addnew()
$objRecordSet.Fields.item("Spalte1")="Inhalt"
$objRecordSet.Fields.item("Spalte2")="Inhalt"
# usw. - je nachdem wie viele Spalten Ihre Tabelle enthält
$objRecordSet.Update()
$objRecordSet.close()
$objCon.close()
Auch hier sind die von Ihnen anzupassenden Stellen wieder fett markiert.

Datensatz aus einer Tabelle löschen

Mit dem SQL-Kommando werden die zu löschenden Datensätze ausgewählt und anschließend durch die delete Methode gelöscht.

# Datenbankverbindung herstellen
$strDB="C:\db\Services.mdb"
$strTable="Services"
$objCon=New-Object -comobject ADODB.Connection
$objRecordSet=New-Object -comobject ADODB.Recordset
$objCon.Open("Provider = Microsoft.Jet.OLEDB.4.0; Data Source = $strDB")
# Durch den SQL-Befehl werden die zu löschenden Datensätze ausgewählt
$objRecordSet.Open("SELECT * FROM Services WHERE Dienstname='Appinfo'",$objCon,3,3)
# und dann darauf die Delete-Methode angewendet
$objRecordSet.delete()
# Datenbankverbindung lösen
$objRecordSet.close()
$objCon.close()
write-host
write-host "Datensatz gelöscht!"

Powershell GUI Programmierung für Dummies Step-by-Step

Ich habe ein Weilchen überlegt, ob ich eine Modulsammlung mit einfachen Befehlen schreibe, oder lieber die GUI-Programmierung erkläre. Da es jede Menge Codebeispiele im Internet gibt um auf die Schnelle das ein oder andere grafische Element zu erstellen, habe ich mich für die Erklärung entschieden, da dies wesentlich mehr Möglichkeiten zur eigenen Anpassung bietet. Für ganz Eilige habe ich ein Modul programmiert, welches ein paar grundsätzliche Funktionen bereit stellt. Die finden Sie am Ende des Artikels.

Grundgerüst

Grundsätzlich brauchen Sie in jedem grafischen Element folgenden Aufbau:

# Die ersten beiden Befehle holen sich die .NET-Erweiterungen (sog. Assemblys) für die grafische Gestaltung in den RAM.

 

 

 

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 
# Die nächste Zeile erstellt aus der Formsbibliothek das Fensterobjekt.
$objForm = New-Object System.Windows.Forms.Form
# Die letzte Zeile sorgt dafür, dass unser Fensterobjekt auf dem Bildschirm angezeigt wird. Das muss immer am Ende stehen.
[void] $objForm.ShowDialog()
Zugegeben, dass ist nun wirklich nicht gerade weltbewegend, aber es soll einfach mal klar machen, was eben das Grundgerüst eines jeden GUI-Elements darstellt. Das [void] vor den .NET-Aufrufen sorgt dafür, dass Rückgabewerte aus dem .NET verschluckt werden. So wie wir es zurück bekommen würde es auch unser Script ausgeben und das wollen wir normaler Weise nicht (insbesondere wenn Sie Ihre GUI-Elemente als Funktion verpacken)!

Position und Größe festlegen

Alle weiteren Befehle tragen Sie in Ihr Script bitte nach der grün gekennzeichneten Zeile ein, da diese Zeile das Fensterobjekt erstellt und alle weiteren Codeschnippsel unser Hauptfenster anpassen. Sind unsere Anpassungen abgeschlossen, sorgen wir mit der blauen Zeile für die Anzeige unseres fertigen Objekts. Die Zeile mit der Eigenschaft Startposition legt fest, wo auf dem Bildschirm unser Fenster auftauchen soll.

$objForm.StartPosition = "CenterScreen"

Wenn Sie auf $objForm ein get-member folgen lassen, also $objForm | gm in einer Zeile nach der grünen, dann sehen Sie die gesamten Events, Methoden und Eigenschaften, die Sie mit dem Fenster verwenden können. Dort werden Sie auch die Eigenschaft (Property) StartPosition finden. In der rechten Spalte sehen Sie dann die Objektklasse System.Windows.Forms.FormStartPosition welche die Funktionen bereit stellt. Vielleicht haben Sie sich auch gefragt welche Werte Sie sonst noch verfüttern können außer CenterScreen. Sorry, aber ich kenne auch nicht das komplette .NET-Framework auswendig, also fragen wir doch einfach die Powershell mittels der Klasse System.Enum in Form von:

[System.Enum]::GetNames("System.Windows.Forms.FormStartPosition")

Das müssen Sie gar nicht im Script eintippen, sondern das können Sie ganz normal in die Konsole eintippen. Leider funktioniert dies nicht bei allem Objekteigenschaften, sondern nur bei denen mit Stichworten. Aber das ist besser als nichts. Eine detaillierte Erklärung der einzelnen Werte von Eigenschaft finden Sie bei MSDN. Googeln Sie einfach nach der .NET-Klasse, also beispielsweise: System.Windows.Forms.FormStartPosition und klicken Sie auf den Link bei dem in der URL MSDN mit drin steht.

Die Cleveren unter Ihnen haben sich bestimmt schon gefragt, warum das Assembly System.Drawing geladen wird, da das obere Beispiel auch ohne die Zeile mit dem DrawingAssembly funktioniert. Das Drawing Assembly ermöglicht uns Position (dazu später mehr) und Größe der grafischen Elemente zu bestimmen. Die Größe ist nämlich ein eigenes Unterobjekt von unserem Fenster. So ähnlich wie am „Objekt“ Mensch auch Unterobjekte wie Arme und Beine dran sind. $objForm ist also unser Fenster und der Eigenschaft Size erzeugen wir nun ein eigenes Objekt, das für die Verwaltung der Größe zuständig ist. In diesem Fall ist unser Fenster in der Ausgangsversion also 300 Bildpunkt breit und 200 Bildpunkte hoch.

$objForm.Size = New-Object System.Drawing.Size(300,200)

Das Fenster können Sie wie ein übliches Anwendungsfenster nun auch kleiner oder größer ziehen oder durch festhalten der Titelleiste die positionieren. Die Fensterknöpfe zum Minimieren (in die Taskleiste) und Maximieren als auch zum schließen funktionieren auch bereits, ohne dass Sie dafür etwas hätten tun müssen. Aber das das mit dem Schließen funktioniert haben Sie bestimmt sowieso schon herausgefunden. 😉

Titelleiste

Die Titelleiste beschriften können Sie mit:

$objForm.Text = "Überschrift"

Textinhalt

Um einen Text ins Fenster hinein zu schreiben brauchen wir die Objektklasse Label aus dem Forms Assembly.

$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,50) 
$objLabel.Size = New-Object System.Drawing.Size(100,20) 
$objLabel.Text = "Hallo Welt"
$objLabel.Name = "Hallo Welt"
$objForm.Controls.Add($objLabel)

In der ersten Zeile wird ein neues Objekt aus der Objektklasse Label erstellt. Dies hat mit unserem Hauptfenster an dieser Stelle noch gar nichts zu tun!
In der zweiten Zeile greifen wir nun wieder in das Drawingsassembly und erstellen ein Objekt Location und kleben das an unser Labelobjekt dran. Durch die Werte 10 und 50 wird ein Offset festgelegt um wie viele Bildschirmpunkte das Label später bei der Anzeige nach rechts (10 Punkte) und nach unten (50 Punkte) versetzt angezeigt werden soll.
In der dritten Zeile kleben Sie in ähnlicher Form nun das Objekt Size an unseren Text. Damit reservieren Sie die Größe Ihrer Textbox. Sollte Ihr Text nicht komplett angezeigt werden kann dies daran liegen, dass der Bereich den Sie reserviert haben nicht groß genug für die Anzeige ist. Hier wurden 100 Bildpunkte horizontal und 20 Bildpunkte vertikal vorgegeben.
In der vierten Zeile legen Sie dann endlich den darzustellenden Text fest. Der kann natürlich auch gerne in Form einer Variablen übergeben werden.
In der fünften Zeile geben Sie dem Objekt einen Namen. Das könnte in diesem Beispiel auch entfallen, aber wenn man aus anderen Programmteilen später darauf zurückgreifen möchte, ist es immer gut einen Namen festzulegen.
In der letzten Zeile wird nun unser Labelobjekt ($objLabel) in unser Hauptfenster ($objForm) eingebaut.

Wenn Sie sich verwundern, warum die Positionen und Größen im Beispiel so seltsam sind, machen Sie doch gleich eine kleine Übung mit eigenen Texten und rücken Sie sich alles schön zurecht.

Buttons

Dann wollen wir doch noch einen Knopf annähen?!

$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,120)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.Name = "OK"
$OKButton.DialogResult = "OK"
$OKButton.Add_Click({$objForm.Close()})
$objForm.Controls.Add($OKButton) 

Die ersten fünf Zeilen sind denen aus dem Beispiel Textinhalt ähnlich. Daher nur die Information, dass wir hier auf die Objektklasse Button zurückgreifen um einen Knopf zu erstellen. Wer hätte das gedacht? 😉
Erklärungsbedürftig ist dafür dann Zeile 5+6! In Zeile 5 geben Sie den Wert an, der dem aufrufenden Objekt bei einem Klick zurück geliefert werden soll. Nach einm Klick auf einen Knopf wird dann der beim Knopf gesetzte Wert von DialogResult in die Eigenschaft Dialogresult von $objForm geschrieben.
Wenn Sie auf $OKButton oder $objForm ein get-member absetzen finden Sie bei beiden den Event Click. Sie können also nicht nur auf den Knopf, sondern auch am Fenster insgesamt abfragen, ob man es angeklickt hat. Bleiben wir bei unserem Knopf! Um den Knopf mit einer Funktion auszustatten kleben Sie noch das Click Ereignis an den Knopf dran (Zeile 6). Selbstverständlich gehen auch die anderen Events wie MouseDoubleClick, Mousehover, MouseLeave, etc… was auch immer Sie vorhaben. Grundsätzlich passiert dies durch $OKButton.Add_Click({}). In den Klammern steht dann Ihr Powershellscriptcode den Sie ausführen möchten, wenn der Knopf angeklickt wurde. Im Beispiel steht in der geschweiften Klammer $objForm.Close(). Durch $objForm.Close() schließen Sie das Hauptfenster.
In der letzten Zeile wird der Knopf an das Hauptfenster geklebt.

Wenn Sie nun ganz am Ende des Scripts, also nach dem ShowDialog einfach $objForm.DialogResult hinten dran schreiben, wird der Text den Sie bei $OKButton.DialogResult bei anklicken zugewiesen haben auf der Konsole ausgegeben. Natürlich können Sie mit $OKButton.DialogResult auch alles andere anstellen. Es ist ja „einfach nur eine Textvariable“. Falls Sie größere Codeabschnitte beim anklicken ausführen möchten können Sie natürlich auch entweder über Funktionen arbeiten, oder es so schreiben:

$OKButton.Add_Click({
 # ....weiterer Code
})

So, nun haben Sie einen OK-Knopf! Wie wäre es, wenn Sie den Code-Abschnitt des OK-Knopfes einfach noch ein 2. Mal unter den OKButton kopieren, die Zahlen für die Positionierung anpassen und die Variable $OKButton durch $CancelButton im kopierten Abschnitt ersetzen. Schon haben Sie zwei Knöpfchen. Auch der Code, der durch Klick auf den CancelButton ausgeführt wird sollte angepasst werden, damit unterschiedliche Werte für $objForm.DialogResult zurück geliefert werden, je nachdem was angeklickt wird.

Sollen die beiden Knöpfe nun auch bei Enter (= OK) oder Escape (= Cancel) ausgelöst werden bietet sich folgendes an:

$objForm.KeyPreview = $True
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter") { $ObjForm.DialogResult="OK";$objForm.Close()} })
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape") { $ObjForm.DialogResult="Cancel";$objForm.Close()} })

Hier wird der KeyDown Event noch an Ihr Fenster montiert. Wird dann bei ausgelöstem Keydown Ereignis (if $_.KeyCode -eq „Enter“) Enter gedrückt, wird in $ObjForm.DialogResult wieder „OK geklickt“ rein geschrieben und falls die gedrückte Taste „Escape“ (ESC) ist (if $_.KeyCode -eq „Escape“) dann kommt eben „Cancel geklickt“ in $ObjForm.DialogResult. Bei beiden Knöpfen wird auch noch das Fenster geschlossen durch $objForm.Close(). $objForm.KeyPreview = $True müssen immer mit dazu schreiben, sonst klappt das eher schlecht als recht.

Texteingabefeld

Möchten Sie Texte oder Zahlen vom Benutzer abfragen, können Sie das natürlich auch über benutzerfreundliche Fenster tun. Alles was Sie dazu brauchen ist folgender Code in dem o.a. Grundgerüst:

$objTextBox = New-Object System.Windows.Forms.TextBox 
$objTextBox.Location = New-Object System.Drawing.Size(10,100) 
$objTextBox.Size = New-Object System.Drawing.Size(100,20) 
$objForm.Controls.Add($objTextBox)

Damit bauen Sie sich ein Eingabefeld dazu. Eine Erklärung ist hier wohl überflüssig, was die einzelnen Zeilen bedeuten. Schön, dass wir nun ein Eingabefeld haben, aber wo wird das, was der Benutzer eingetippt hat hinterlegt? In:

$objTextBox.Text

Wenn Sie bei dem ScriptBlock für das Eingabefeld die Zeile:

 $objTextBox.Text = "Vorgabe"

ergänzen, können Sie schon einen Standardtext vor belegen lassen. Das komplette Script könnte also aktuell etwa so aussehen:

[void] [System.Reflection.Assembly]::LoadWithPartialName(„System.Drawing“)
[void] [System.Reflection.Assembly]::LoadWithPartialName(„System.Windows.Forms“)
$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = „Dateneingabe“
$objForm.Size = New-Object System.Drawing.Size(400,300)
$objForm.StartPosition = „CenterScreen“
$objForm.KeyPreview = $True
$objForm.Add_KeyDown({if ($_.KeyCode -eq „Enter“) { $objForm.DialogResult=“OK“;$objForm.Close()} })
$objForm.Add_KeyDown({if ($_.KeyCode -eq „Escape“) { $objForm.DialogResult=“Cancel“;$objForm.Close()} })

$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,120)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = „OK“
$OKButton.DialogResult = „OK“
$OKButton.Add_Click({$objForm.Close()})
$objForm.Controls.Add($OKButton)

$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(150,120)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = „Cancel“
$CancelButton.DialogResult = „Cancel“
$CancelButton.Add_Click({$objForm.Close()})
$objForm.Controls.Add($CancelButton)

$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,20)
$objLabel.Size = New-Object System.Drawing.Size(280,20)
$objLabel.Text = „Bitte geben Sie etwas ein:“
$objForm.Controls.Add($objLabel)
$objTextBox = New-Object System.Windows.Forms.TextBox
$objTextBox.Location = New-Object System.Drawing.Size(10,40)
$objTextBox.Size = New-Object System.Drawing.Size(260,20)
$objTextBox.Text = „Vorgabe“
$objForm.Controls.Add($objTextBox)

[void] $objForm.ShowDialog()

If ($objForm.DialogResult -like „OK“) {$objTextBox.Text} else {„Abbruch geklickt“}

Auf dieselbe Art und Weise können Sie auch mehrere Textbereiche einfügen. Genauso wie mit den Knöpfen.

Auswahlliste

Wie wäre es mit einer Auswahlliste? Hier sind die Grundlagen wieder identisch. Im vollständigen nachfolgenden Script sind die Besonderheiten der Listbox orange hervorgehoben:

 

 

 

 

 

 

 

 $x = @()
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 
$objForm = New-Object System.Windows.Forms.Form 
$objForm.Text = "Data Entry Form"
$objForm.Size = New-Object System.Drawing.Size(300,200) 
$objForm.StartPosition = "CenterScreen"
$objForm.KeyPreview = $True $objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter") 
     {
         foreach ($objItem in $objListbox.SelectedItems)
             {$x += $objItem}
         $objForm.Close()
     }
    })
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape") 
     {$objForm.Close()}})
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,120)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.Add_Click(
    {
         foreach ($objItem in $objListbox.SelectedItems)
             {$x += $objItem}
         $objForm.Close()
    })
$objForm.Controls.Add($OKButton)
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(150,120)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.Add_Click({$objForm.Close()})
$objForm.Controls.Add($CancelButton)
$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,20) 
$objLabel.Size = New-Object System.Drawing.Size(280,20) 
$objLabel.Text = "Suchen Sie sich etwas aus:"
$objForm.Controls.Add($objLabel) 
$objListbox = New-Object System.Windows.Forms.Listbox 
$objListbox.Location = New-Object System.Drawing.Size(10,40) 
$objListbox.Size = New-Object System.Drawing.Size(260,20) 
$objListbox.SelectionMode = "MultiExtended"
[void] $objListbox.Items.Add("1. Element")
[void] $objListbox.Items.Add("2. Element")
[void] $objListbox.Items.Add("3. Element")
[void] $objListbox.Items.Add("4. Element")
[void] $objListbox.Items.Add("5. Element")
$objListbox.Height = 70
$objForm.Controls.Add($objListbox) 
[void] $objForm.ShowDialog()
$x

1. orange Zeile: $x wird hier als Array und nicht als String definiert, da unter Umständen es mehrere Elemente die angeklickt (mit Strg-Taste) wurden übergeben muss.

Darauf folgen 2 orange Codeabschnitte, die identisch sind. Der erste enthält die Reaktion für das Drücken der Enter-Taste, der zweite die Reaktion für einen Klick auf den OK-Knopf. Da bei beiden Aktionen dasselbe stattfinden soll steht natürlich auch derselbe Inhalt drin. Die Erklärung hierfür möchte ich aber erst einmal hinten anstellen und zunächst den 3. orangen Codeabschnitt erklären in dem das Auswahlfeld zusammen gebaut wird.

Auch hier erstellen wir uns in der 1. Zeile aus dem Windows.Forms Assembly zunächst das ListBox Objekt. Aus Zeile 2. und 3. Size und Location sollte an dieser Stelle klar sein (siehe weiter oben).
In Zeile 4 finden wir den sog. SelectionMode. Damit können Sie festlegen, ob mehrere Elemente oder nur eines ausgewählt werden können. Welche weiteren Alternativen außer MultiExtended noch zur Verfügung können Sie wieder über die Klasse System.Enum herausfinden (weiter oben beschrieben).
In den darauf folgenden Zeilen werden die einzelnen darzustellenden Elemente hinzugefügt. Selbstverständlich können Sie anstatt den darzustellenden Text direkt einzugeben auch Variablen übergeben. Dann aber bitte nur die Klammern, ohne die Anführungszeichen! Eleganter wäre in der Form vielleicht eine Schleife gewesen. Das können Sie ja als kleine Übung betrachten. 😉
Listbox.Height legt die vertikale Größe der Listbox fest. Eigentlich passiert das doch durch die Size Anweisung aus dem Drawing-Assembly?! Kommentieren Sie die Zeile doch einfach einmal aus und starten Sie das Script. Das ist bestimmt eine interessante Überraschung!
Zu guter Letzt wird unsere Listbox ins Fenster geschraubt.

So, nun zu dem was passiert, wenn OK geklickt, oder Enter gedrückt wird. Zunächst einmal wird eine foreach-Schleife angelegt, die so lange durchlaufen wird, wie es ausgewählte Elemente gibt. In $objListbox.SelectedItems sind alle ausgewählten Elemente enthalten. Diese werden dann der Reihe nach bei jedem Schleifendruchlauf zu $objItem. Im Schleifenrumpf werden dann die Werte in den Array $x eingespeist. Sind alle Elemte in den Array überführt ist die Schleife zu Ende und es wird $objForm.Close() ausgeführt, was das Fenster schließt.

Am Ende des Scripts wird dann wieder $x als Rückgabewert mit den ausgewählten Elementen ausgegeben.

Dropdownfeld

Ein Dropdownfeld oder auch Combox genannt funktioniert ähnlich wie die Auswahlliste. Sie müssen lediglich die Zeile mit „MultiExtended“ entfernen (eine Mehrfachauswahl wird vom Dropdownfeld nicht unterstützt), den Begriff Listbox gegen Combobox tauschen und bei den selected-items Einträgen das s am Schluß entfernen. Also ist es eigentlich sogar einfacher als die Auswahlliste. Hier das komplette Script:

 

 

 

 

 

 

 

 

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 
$objForm = New-Object System.Windows.Forms.Form 
$objForm.Text = "Combobox"
$objForm.Size = New-Object System.Drawing.Size(300,200) 
$objForm.StartPosition = "CenterScreen"
$objForm.KeyPreview = $True
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter") 
     {
         foreach ($objItem in $objCombobox.SelectedItem)
             {$x += $objItem}
         $objForm.Close()
     }
    })
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape") 
     {$objForm.Close()}})
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,120)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.Add_Click(
    {
         foreach ($objItem in $objCombobox.SelectedItem)
             {$x += $objItem}
         $objForm.Close()
    })
$objForm.Controls.Add($OKButton)
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(150,120)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.Add_Click({$objForm.Close()})
$objForm.Controls.Add($CancelButton)
$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,20) 
$objLabel.Size = New-Object System.Drawing.Size(280,20) 
$objLabel.Text = "Treffen Sie bitte eine Auswahl:"
$objForm.Controls.Add($objLabel) 
$objCombobox = New-Object System.Windows.Forms.Combobox 
$objCombobox.Location = New-Object System.Drawing.Size(10,40) 
$objCombobox.Size = New-Object System.Drawing.Size(260,20) 
[void] $objCombobox.Items.Add("Item 1")
[void] $objCombobox.Items.Add("Item 2")
[void] $objCombobox.Items.Add("Item 3")
[void] $objCombobox.Items.Add("Item 4")
[void] $objCombobox.Items.Add("Item 5")
$objCombobox.Height = 70
$objForm.Controls.Add($objCombobox) 
$objForm.Topmost = $True
$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()
$x

Kalenderauswahl

Benutzer nach einem Datum fragen ist eine Kunst für sich! Da kann man tausend Beispiele schreiben wie das Datum einzugeben ist und trotzdem machen sie es falsch. Abgesehen davon hat man die Probleme mit Schaltjahren und sonstigem Brimbamborium rund um die Datumsverwaltung. Obendrein müsste man den Text erst in ein Datumsformat konvertieren. Das ist alles gaaaanz gruselig. Also machen wir es uns einfach!

 

 

 

 

 

 

 

 

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 
$objForm = New-Object Windows.Forms.Form
$objForm.Text = "Wählen Sie ein Datum aus" 
$objForm.Size = New-Object System.Drawing.Size (190,200) 
$objForm.StartPosition = "CenterScreen"
$objForm.KeyPreview = $True $objForm.Add_KeyDown({
     if ($_.KeyCode -eq "Enter") 
         {
             $WahlDatum=$objCalendar.SelectionStart
             $objForm.Close()
         }
     })
$objForm.Add_KeyDown({
     if ($_.KeyCode -eq "Escape") 
         {
             $objForm.Close()
         }
     })
$objCalendar = New-Object System.Windows.Forms.MonthCalendar 
$objCalendar.ShowTodayCircle = $False
$objCalendar.MaxSelectionCount = 1
$objForm.Controls.Add($objCalendar) 
[void] $objForm.ShowDialog() 
if ($WahlDatum)
     {
         Write-Host "Ausgewähltes Datum: $WahlDatum"
     }

Fertig ist der Kalender! Auch hier habe die neuen Stellen wieder orange markiert. Der Einfachheit halber sind keine OK oder Cancel-Buttons eingebaut. Es reagiert nur auf Enter oder ESC. Diese Funktion können Sie aber gerne als kleine Vertiefung nachrüsten. 😉

Die erste eingefärbte Zeile holt aus dem Kalenderobjekt (das wir weiter unten erstellen) das ausgewählte Datum ab und schreibt es in die Variable $WahlDatum. Das drumherum sollten Sie erkennen. Es sorgt dafür, dass dies nur passiert, wenn jemand Enter gedrückt hat.

Im nach folgenden Block wird das Kalenderobjekt erzeugt und eingestellt. ShowTodayCircle=$False blendet nur ein hässliches Viereck aus. MaxSelectionCount=1 sorgt dafür, dass nur ein Datum abgeholt wird. Der Rest des Blocks sollte klar sein.

Am Ende wird nur dann ein Datum zurückgegeben, wenn auch eins ausgewählt wurde.

Baumansicht

Die sog. Baumansicht oder auch Treeview genannt läßt sich auch recht einfach implementieren:

[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
$form1 = New-Object System.Windows.Forms.Form
$treeView1 = New-Object System.Windows.Forms.TreeView
$InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState
 
$form1.Text = "Überschrift"
$form1.Name = "form1"
$form1.DataBindings.DefaultDataSourceUpdateMode = 0
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Width = 284
$System_Drawing_Size.Height = 262
$form1.ClientSize = $System_Drawing_Size
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Width = 260
$System_Drawing_Size.Height = 238
$treeView1.Size = $System_Drawing_Size
$treeView1.Name = "treeView1"
$System_Windows_Forms_TreeNode_1 = New-Object System.Windows.Forms.TreeNode
$System_Windows_Forms_TreeNode_2 = New-Object System.Windows.Forms.TreeNode
$System_Windows_Forms_TreeNode_3 = New-Object System.Windows.Forms.TreeNode
$System_Windows_Forms_TreeNode_3.Text = "SubSub"
$System_Windows_Forms_TreeNode_3.Name = "Knoten2"
$System_Windows_Forms_TreeNode_2.Nodes.Add($System_Windows_Forms_TreeNode_3)|Out-Null
$System_Windows_Forms_TreeNode_2.Text = "SubA"
$System_Windows_Forms_TreeNode_2.Name = "Knoten1"
$System_Windows_Forms_TreeNode_1.Nodes.Add($System_Windows_Forms_TreeNode_2)|Out-Null
$System_Windows_Forms_TreeNode_4 = New-Object System.Windows.Forms.TreeNode
$System_Windows_Forms_TreeNode_4.Text = "SubB"
$System_Windows_Forms_TreeNode_4.Name = "Knoten3"
$System_Windows_Forms_TreeNode_1.Nodes.Add($System_Windows_Forms_TreeNode_4)|Out-Null
$System_Windows_Forms_TreeNode_5 = New-Object System.Windows.Forms.TreeNode
$System_Windows_Forms_TreeNode_5.Text = "SubC"
$System_Windows_Forms_TreeNode_5.Name = "Knoten4"
$System_Windows_Forms_TreeNode_1.Nodes.Add($System_Windows_Forms_TreeNode_5)|Out-Null
$System_Windows_Forms_TreeNode_1.Text = "SubB"
$System_Windows_Forms_TreeNode_1.Name = "Knoten0"
$treeView1.Nodes.Add($System_Windows_Forms_TreeNode_1)|Out-Null
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 12
$System_Drawing_Point.Y = 12
$treeView1.Location = $System_Drawing_Point
$treeView1.DataBindings.DefaultDataSourceUpdateMode = 0
$treeView1.TabIndex = 0
$form1.Controls.Add($treeView1)
$InitialFormWindowState = $form1.WindowState
$form1.add_Load($OnLoadForm_StateCorrection)
$form1.ShowDialog()| Out-Null
$treeView1.SelectedNode

Sollten Sie noch eine Erklärung benötigen hinterlassen Sie doch einen Kommentar.

Statusleiste und Karteikartenreiter

Hier noch ein kleines Beispiel für Karteikartenreiter und eine Statusleiste. Erklärung durch Kommentarzeilen im Script:

 [reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
$form1 = New-Object System.Windows.Forms.Form
$statusBar1 = New-Object System.Windows.Forms.StatusBar
$tabControl1 = New-Object System.Windows.Forms.TabControl
$tabPage1 = New-Object System.Windows.Forms.TabPage
$comboBox1 = New-Object System.Windows.Forms.ComboBox
$tabPage2 = New-Object System.Windows.Forms.TabPage
$button1 = New-Object System.Windows.Forms.Button
$form1.Text = "Reiter und Statusleiste"
$form1.Name = "form1"
$form1.DataBindings.DefaultDataSourceUpdateMode = 0
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Width = 284
$System_Drawing_Size.Height = 262
$form1.ClientSize = $System_Drawing_Size
# Statusleiste anlegen und mit Ausgangswert (Text) belegen
$statusBar1.Name = "statusBar1"
$statusBar1.Text = "Auswahl: Noch nicht getroffen"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Width = 284
$System_Drawing_Size.Height = 22
$statusBar1.Size = $System_Drawing_Size
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 0
$System_Drawing_Point.Y = 240
$statusBar1.Location = $System_Drawing_Point
$statusBar1.DataBindings.DefaultDataSourceUpdateMode = 0
$statusBar1.TabIndex = 1
$form1.Controls.Add($statusBar1)
# Auswahlreiter anlegen
$tabControl1.TabIndex = 0
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Width = 228
$System_Drawing_Size.Height = 162
$tabControl1.Size = $System_Drawing_Size
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 23
$System_Drawing_Point.Y = 35
$tabControl1.Location = $System_Drawing_Point
$tabControl1.DataBindings.DefaultDataSourceUpdateMode = 0
$tabControl1.Name = "tabControl1"
$tabControl1.SelectedIndex = 0
$form1.Controls.Add($tabControl1)
# Auswahl Seite anlegen und an Auswahlreiter binden
$tabPage1.TabIndex = 0
$tabPage1.UseVisualStyleBackColor = $True
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Width = 220
$System_Drawing_Size.Height = 136
$tabPage1.Size = $System_Drawing_Size
$tabPage1.Text = "Auswahlreiter"
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 4
$System_Drawing_Point.Y = 22
$tabPage1.Location = $System_Drawing_Point
$System_Windows_Forms_Padding = New-Object System.Windows.Forms.Padding
$System_Windows_Forms_Padding.All = 3
$System_Windows_Forms_Padding.Bottom = 3
$System_Windows_Forms_Padding.Left = 3
$System_Windows_Forms_Padding.Right = 3
$System_Windows_Forms_Padding.Top = 3
$tabPage1.Padding = $System_Windows_Forms_Padding
$tabPage1.Name = "tabPage1"
$tabPage1.DataBindings.DefaultDataSourceUpdateMode = 0
$tabControl1.Controls.Add($tabPage1)
# ComboBox auf Auswahltabseite kleben
$comboBox1.FormattingEnabled = $True
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Width = 121
$System_Drawing_Size.Height = 21
$comboBox1.Size = $System_Drawing_Size
$comboBox1.DataBindings.DefaultDataSourceUpdateMode = 0
$comboBox1.Name = "comboBox1"
$comboBox1.Items.Add("Eins")|Out-Null
$comboBox1.Items.Add("Zwei")|Out-Null
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 18
$System_Drawing_Point.Y = 29
$comboBox1.Location = $System_Drawing_Point
$comboBox1.TabIndex = 0
$comboBox1.add_DropDownClosed({$statusBar1.Text = "Auswahl: "+$comboBox1.SelectedItem})
$tabPage1.Controls.Add($comboBox1)
# Beendenreiter anlegen
$tabPage2.TabIndex = 1
$tabPage2.UseVisualStyleBackColor = $True
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Width = 220
$System_Drawing_Size.Height = 136
$tabPage2.Size = $System_Drawing_Size
$tabPage2.Text = "Beenden"
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 4
$System_Drawing_Point.Y = 22
$tabPage2.Location = $System_Drawing_Point
$System_Windows_Forms_Padding = New-Object System.Windows.Forms.Padding
$System_Windows_Forms_Padding.All = 3
$System_Windows_Forms_Padding.Bottom = 3
$System_Windows_Forms_Padding.Left = 3
$System_Windows_Forms_Padding.Right = 3
$System_Windows_Forms_Padding.Top = 3
$tabPage2.Padding = $System_Windows_Forms_Padding
$tabPage2.Name = "tabPage2"
$tabPage2.DataBindings.DefaultDataSourceUpdateMode = 0
$tabControl1.Controls.Add($tabPage2)
# Beenden Seite anlegen und an Beendenreiter binden
$button1.TabIndex = 0
$button1.Name = "button1"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Width = 75
$System_Drawing_Size.Height = 23
$button1.Size = $System_Drawing_Size
$button1.UseVisualStyleBackColor = $True
$button1.Text = "Fertig"
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 28
$System_Drawing_Point.Y = 51
$button1.Location = $System_Drawing_Point
$button1.DataBindings.DefaultDataSourceUpdateMode = 0
$button1.add_Click({$form1.close();write-host $comboBox1.SelectedItem})
$tabPage2.Controls.Add($button1)
# Fenster anzeigen
$form1.ShowDialog()| Out-Null

Anregungen

Weiter oben hatte ich schon einmal erwähnt, dass man mit get-member ganz interessante Dinge über die Objekte herausfinden kann. Wenn Sie in Ihr Script z.B. bei dem Objekt $objForm | get-memeber einfügen werden Sie feststellen, dass man auch die Eigenschaften Fore- und Backcolor einstellen kann. Schreiben Sie doch einfach mal eine Zeile wie $objForm.Backcolor=“Yellow“ in Ihr Script. Welche Farben gibt’s? Nun, das ist doch sicher eine begrenzte Liste an Farben…Aus welcher Objektklasse die Farbe bereit gestellt wird steht beim get-member ja in der rechten Spalte. Ja, richtig! Da steht System.Drawing.Color als Objektklasse. Wie wäre es nun mit einem [System.Enum]::GetNames(„System.Drawing.Color“)? Schwupps…schon wissen Sie welche Farben Sie einstellen können. Ein Farbauswahlbox gibt es auch schon fertig im .NET-Laden zu „kaufen“:

[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
$colorDialog1 = New-Object System.Windows.Forms.ColorDialog
$colorDialog1.ShowDialog()
$colorDialog1.color

Auch das folgende Script sollte noch ein paar Anregungen geben:

function GenerateForm {
 # Assemblys laden
 [reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
 [reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
 # Objekte für Formen generieren
 $form1 = New-Object System.Windows.Forms.Form
 $label1 = New-Object System.Windows.Forms.Label
 $Open = New-Object System.Windows.Forms.Button
 $Cancle = New-Object System.Windows.Forms.Button
 $OK = New-Object System.Windows.Forms.Button
 $progressBar1 = New-Object System.Windows.Forms.ProgressBar
 $GruppeA = New-Object System.Windows.Forms.GroupBox
 $radioButton3 = New-Object System.Windows.Forms.RadioButton
 $radioButton2 = New-Object System.Windows.Forms.RadioButton
 $radioButton1 = New-Object System.Windows.Forms.RadioButton
 $GruppB = New-Object System.Windows.Forms.GroupBox
 $checkBox3 = New-Object System.Windows.Forms.CheckBox
 $checkBox2 = New-Object System.Windows.Forms.CheckBox
 $checkBox1 = New-Object System.Windows.Forms.CheckBox
 $openFileDialog1 = New-Object System.Windows.Forms.OpenFileDialog
 # Wenn Sie speichern Knöpfe haben möchten: $saveFileDialog1 = New-Object System.Windows.Forms.SaveFileDialog
 $InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState
 $checkbox=0,0,0 # Vorgabe in Form eines Arrays für die Checkboxen
 $radiobutton=1  # Vorgabe in Form einer Variablen für die Radiobuttons
 # Ereigniscode für die einzelnen Knöpfe in Variablen hinterlegt
 $Cancle_OnClick= 
 {
  # Wir wollen gar nicht wissen was mit dem Fenster passiert ist. Nur weg hier... ;-)
  $form1.close()
 }
 $OK_OnClick= 
 {
  # Bei Klick auf OK werden zum Schluß ein paar Status-Infos zurück gegeben
  $form1.close()
  write-host "Radiobutton: "$Radiobutton
  write-host "Checkboxstatus: "$checkbox -nonewline
  write-host
  write-host $label1.Text
  write-host "Fortschritt: "$progressBar1.Value
 }
 $Open_OnClick= 
 {
  # Hier wird der Datei öffnen Dialog erstellt. Ein openFileDialog1 | gm verrät mehr.
  $openFileDialog1.ShowHelp = $True
  $openFileDialog1.Title = "Datei öffnen"
  $openFileDialog1.InitialDirectory = "c:"
  $openfileDialog1.ShowDialog()
  $label1.Text=$openFileDialog1.FileName
  # In den nachfolgenden 5 Zeilen wird der Forschrittsbalken bei jeder Aktion etwas weitergerückt bis er voll ist und dann auf 0 zurück gesetzt
  if ($progressBar1.Value -lt 100) {
   $progressBar1.Value += 10
  } else {
   $progressBar1.Value = 0
  }
 }
 $OnLoadForm_StateCorrection=
 {# Damit das Fenster nicht versehentlich maximiert startet.
     $form1.WindowState = $InitialFormWindowState
 }
 $form1.BackgroundImageLayout = 3 # Hintergrundbild gekachelt, gestreckt, oder wie hätten wir's denn gerne?
 $form1.Font = New-Object System.Drawing.Font("Mistral",8.25,0,3,0) # Schriftart festlegen
 $form1.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon('C:\Pfad\IconDatei.ico') # Bild für das Icon festlegen
 $form1.Text = "Überschrift"
 $form1.Name = "form1"
 $form1.BackgroundImage = [System.Drawing.Image]::FromFile('C:\Pfad\Hintergrundbild.jpg') # Hintergrundbild einsetzen
 $form1.DataBindings.DefaultDataSourceUpdateMode = 0
 $form1.ForeColor = [System.Drawing.Color]::FromArgb(255,0,0,255) # Schriftfarbe festlegen
 $System_Drawing_Size = New-Object System.Drawing.Size
 $System_Drawing_Size.Width = 332
 $System_Drawing_Size.Height = 264
 $form1.ClientSize = $System_Drawing_Size
 $form1.Opacity = 0.8 # Legt die Durchsichtigkeit fest 1=100% 0.1=10%
 $label1.TabIndex = 6 # Tabindex legt die Tabulatorsprungreihenfolge fest
 $System_Drawing_Size = New-Object System.Drawing.Size
 $System_Drawing_Size.Width = 100
 $System_Drawing_Size.Height = 23
 $label1.Size = $System_Drawing_Size
 $label1.Text = "Keine Datei ausgewählt"
 $System_Drawing_Point = New-Object System.Drawing.Point
 $System_Drawing_Point.X = 24
 $System_Drawing_Point.Y = 184
 $label1.Location = $System_Drawing_Point
 $label1.DataBindings.DefaultDataSourceUpdateMode = 0
 $label1.Name = "label1"
 $form1.Controls.Add($label1)
 $Open.TabIndex = 5
 $Open.Name = "Open"
 $System_Drawing_Size = New-Object System.Drawing.Size
 $System_Drawing_Size.Width = 91
 $System_Drawing_Size.Height = 22
 $Open.Size = $System_Drawing_Size
 $Open.UseVisualStyleBackColor = $True
 $Open.Text = "Open"
 $System_Drawing_Point = New-Object System.Drawing.Point
 $System_Drawing_Point.X = 24
 $System_Drawing_Point.Y = 229
 $Open.Location = $System_Drawing_Point
 $Open.DataBindings.DefaultDataSourceUpdateMode = 0
 $Open.add_Click($Open_OnClick)
 $form1.Controls.Add($Open)
 $Cancle.TabIndex = 4
 $Cancle.Name = "Cancle"
 $System_Drawing_Size = New-Object System.Drawing.Size
 $System_Drawing_Size.Width = 75
 $System_Drawing_Size.Height = 23
 $Cancle.Size = $System_Drawing_Size
 $Cancle.UseVisualStyleBackColor = $True
 $Cancle.Text = "Cancle"
 $System_Drawing_Point = New-Object System.Drawing.Point
 $System_Drawing_Point.X = 243
 $System_Drawing_Point.Y = 229
 $Cancle.Location = $System_Drawing_Point
 $Cancle.DataBindings.DefaultDataSourceUpdateMode = 0
 $Cancle.add_Click($Cancle_OnClick)
 $form1.Controls.Add($Cancle)
 $OK.TabIndex = 3
 $OK.Name = "OK"
 $System_Drawing_Size = New-Object System.Drawing.Size
 $System_Drawing_Size.Width = 75
 $System_Drawing_Size.Height = 23
 $OK.Size = $System_Drawing_Size
 $OK.UseVisualStyleBackColor = $True
 $OK.Text = "OK"
 $System_Drawing_Point = New-Object System.Drawing.Point
 $System_Drawing_Point.X = 150
 $System_Drawing_Point.Y = 229
 $OK.Location = $System_Drawing_Point
 $OK.DataBindings.DefaultDataSourceUpdateMode = 0
 $OK.add_Click($OK_OnClick)
 $form1.Controls.Add($OK)
 # Eine Fortschrittsanzeige, die sich verändert je öfter man den Dateiöffnen Dialog aufruft
 $progressBar1.DataBindings.DefaultDataSourceUpdateMode = 0
 $System_Drawing_Size = New-Object System.Drawing.Size
 $System_Drawing_Size.Width = 100
 $System_Drawing_Size.Height = 23
 $progressBar1.Size = $System_Drawing_Size
 $progressBar1.TabIndex = 2
 $System_Drawing_Point = New-Object System.Drawing.Point
 $System_Drawing_Point.X = 24
 $System_Drawing_Point.Y = 153
 $progressBar1.Location = $System_Drawing_Point
 $progressBar1.Name = "progressBar1"
 $progressBar1.Value = 0 # Die Skala geht von 0-100, die Änderung am Wert wird durch das Click-Ereignis beim Dateiöffnendialog vorgenommen
 $form1.Controls.Add($progressBar1)
 # Durch Einbau der Gruppierung, weiß .NET automatisch, dass innerhalb einer Gruppe immer nur 1 Radiobutton geklickt sein darf im Gegensatz zu den Checkboxen.
 $GruppeA.Name = "GruppeA" # Benamung des linke Gruppenfelds
 $GruppeA.Text = "Gruppe1"  # Überschrift für das linke Gruppenfeld
 $System_Drawing_Size = New-Object System.Drawing.Size
 $System_Drawing_Size.Width = 119
 $System_Drawing_Size.Height = 135
 $GruppeA.Size = $System_Drawing_Size
 $System_Drawing_Point = New-Object System.Drawing.Point
 $System_Drawing_Point.X = 12
 $System_Drawing_Point.Y = 12
 $GruppeA.Location = $System_Drawing_Point
 $GruppeA.BackColor = [System.Drawing.Color]::FromArgb(255,153,180,209) # Hintergrundfarbe festlegen
 $GruppeA.TabStop = $False
 $GruppeA.TabIndex = 0
 $GruppeA.DataBindings.DefaultDataSourceUpdateMode = 0
 $form1.Controls.Add($GruppeA)
 $radioButton3.TabIndex = 2
 $radioButton3.Name = "radioButton3"
 $System_Drawing_Size = New-Object System.Drawing.Size
 $System_Drawing_Size.Width = 148
 $System_Drawing_Size.Height = 31
 $radioButton3.Size = $System_Drawing_Size
 $radioButton3.UseVisualStyleBackColor = $True
 $radioButton3.Text = "radioButton3"
 $System_Drawing_Point = New-Object System.Drawing.Point
 $System_Drawing_Point.X = 16
 $System_Drawing_Point.Y = 86
 $radioButton3.Location = $System_Drawing_Point
 $radioButton3.DataBindings.DefaultDataSourceUpdateMode = 0
 $radioButton3.TabStop = $True
 $radioButton3.add_Click({$radiobutton=3})
 $GruppeA.Controls.Add($radioButton3)
 $radioButton2.TabIndex = 1
 $radioButton2.Name = "radioButton2"
 $System_Drawing_Size = New-Object System.Drawing.Size
 $System_Drawing_Size.Width = 149
 $System_Drawing_Size.Height = 27
 $radioButton2.Size = $System_Drawing_Size
 $radioButton2.UseVisualStyleBackColor = $True
 $radioButton2.Text = "radioButton2"
 $System_Drawing_Point = New-Object System.Drawing.Point
 $System_Drawing_Point.X = 16
 $System_Drawing_Point.Y = 53
 $radioButton2.Location = $System_Drawing_Point
 $radioButton2.DataBindings.DefaultDataSourceUpdateMode = 0
 $radioButton2.TabStop = $True
 $radioButton2.add_Click({$radiobutton=2})
 $GruppeA.Controls.Add($radioButton2)
 $radioButton1.TabIndex = 0
 $radioButton1.Name = "radioButton1"
 $System_Drawing_Size = New-Object System.Drawing.Size
 $System_Drawing_Size.Width = 148
 $System_Drawing_Size.Height = 27
 $radioButton1.Size = $System_Drawing_Size
 $radioButton1.UseVisualStyleBackColor = $True
 $radioButton1.Text = "radioButton1"
 $System_Drawing_Point = New-Object System.Drawing.Point
 $System_Drawing_Point.X = 17
 $System_Drawing_Point.Y = 20
 $radioButton1.Location = $System_Drawing_Point
 $radioButton1.DataBindings.DefaultDataSourceUpdateMode = 0
 $radioButton1.TabStop = $True
 $radioButton1.add_Click({$radiobutton=1})
 $GruppeA.Controls.Add($radioButton1)
 # Bei den Checkboxen hätte man auch auf die Gruppierung verzichten können. Ist so aber übersichtlicher - finde ich.
 $GruppB.Name = "GruppB" # Benamung des rechten Gruppenfelds
 $GruppB.Text = "Gruppe2" # Überschrift für das rechte Gruppenfeld
 $System_Drawing_Size = New-Object System.Drawing.Size
 $System_Drawing_Size.Width = 168
 $System_Drawing_Size.Height = 195
 $GruppB.Size = $System_Drawing_Size
 $System_Drawing_Point = New-Object System.Drawing.Point
 $System_Drawing_Point.X = 150
 $System_Drawing_Point.Y = 12
 $GruppB.Location = $System_Drawing_Point
 $GruppB.BackColor = [System.Drawing.Color]::FromArgb(255,100,149,237)
 $GruppB.TabStop = $False
 $GruppB.TabIndex = 1
 $GruppB.DataBindings.DefaultDataSourceUpdateMode = 0
 $form1.Controls.Add($GruppB)
 $checkBox3.UseVisualStyleBackColor = $True
 $System_Drawing_Size = New-Object System.Drawing.Size
 $System_Drawing_Size.Width = 104
 $System_Drawing_Size.Height = 24
 $checkBox3.Size = $System_Drawing_Size
 $checkBox3.TabIndex = 2
 $checkBox3.Text = "checkBox3"
 $System_Drawing_Point = New-Object System.Drawing.Point
 $System_Drawing_Point.X = 14
 $System_Drawing_Point.Y = 112
 $checkBox3.Location = $System_Drawing_Point
 $checkBox3.DataBindings.DefaultDataSourceUpdateMode = 0
 $checkBox3.Name = "checkBox3"
 $checkBox3.add_Click({$checkbox[2]=1})
 $GruppB.Controls.Add($checkBox3)
 $checkBox2.UseVisualStyleBackColor = $True
 $System_Drawing_Size = New-Object System.Drawing.Size
 $System_Drawing_Size.Width = 104
 $System_Drawing_Size.Height = 24
 $checkBox2.Size = $System_Drawing_Size
 $checkBox2.TabIndex = 1
 $checkBox2.Text = "checkBox2"
 $System_Drawing_Point = New-Object System.Drawing.Point
 $System_Drawing_Point.X = 15
 $System_Drawing_Point.Y = 66
 $checkBox2.Location = $System_Drawing_Point
 $checkBox2.DataBindings.DefaultDataSourceUpdateMode = 0
 $checkBox2.Name = "checkBox2"
 $checkBox2.add_Click({$checkbox[1]=1})
 $GruppB.Controls.Add($checkBox2)
 $checkBox1.UseVisualStyleBackColor = $True
 $System_Drawing_Size = New-Object System.Drawing.Size
 $System_Drawing_Size.Width = 104
 $System_Drawing_Size.Height = 24
 $checkBox1.Size = $System_Drawing_Size
 $checkBox1.TabIndex = 0
 $checkBox1.Text = "checkBox1"
 $System_Drawing_Point = New-Object System.Drawing.Point
 $System_Drawing_Point.X = 15
 $System_Drawing_Point.Y = 23
 $checkBox1.Location = $System_Drawing_Point
 $checkBox1.DataBindings.DefaultDataSourceUpdateMode = 0
 $checkBox1.Name = "checkBox1"
 $checkBox1.add_Click({$checkbox[0]=1})
 $GruppB.Controls.Add($checkBox1)
 # Status des Fensters sichern
 $InitialFormWindowState = $form1.WindowState
 # Die Funktion bei Aufruf des aufrufen
 $form1.add_Load($OnLoadForm_StateCorrection)
 # Fenster anzeigen
 $form1.ShowDialog()| Out-Null
 }

# Aufruf des Fenster, denn es wurde ja in eine Funktion eingebaut
GenerateForm
So, nun haben Sie genug Basiswissen. Noch ein kleiner Tipp: Suchen Sie mal im Internet nach „Primal Forms Community Edition„. Primal Script selbst kostet Geld, aber die Community Edition finden Sie an einigen Ecken des Internet zum kostenlosen Download. Da ich nicht sicher bin, ob man dies einfach so verbreiten darf, habe ich den direkten Link hier leider weg lassen müssen. Damit haben Sie einen grafischen Editor, der Ihnen den Powershellcode für komplexe GUI-Elemente zusammen baut. Sie brauchen Ihn nur noch nach Ihren Wünschen anzupassen.

Wie versprochen für die ganz Eiligen ein Modul mit einfachen Powershellbefehlen um ein Fenster mit Knöpfen, Texten, Inputboxen und sogar einem Kalender zu erstellen. Folgende Befehle werden derzeit unterstützt:

new-guiwindow (legt das Hauptfenster an)
new-guibutton (erstellt einen Knopf)
new-guitextbox (erstellt einen Textabschnitt)
new-guiinputbox (erstellt ein Eingabefeld)
new-guicalendar (erstellt eine Kalenderauswahl für Datumsangaben)
new-guidropdown (erstellt eine DropDownbox)
close-guiwindow (kann interaktiv ein Fenster schliessen)
set-guitextbox (kann interaktiv den Inhalt eine Textbox anpassen)
get-guiinputbox (kann interaktiv den Inhalt aus einer Inputbox abrufen)
show-guiwindow (zeigt das zusammengebaute Fenster an und liefert Rückgabewerte in Form eines Hashs)

Sollte das Zip-File mit den Scripten und Installationshinweisen nicht zum Download angezeigt werden, klicken Sie bitte auf die Artikelüberschrift.

GuiHelper

Wenn Sie weitere interessante Praxistipps zu PowerShell lesen möchten oder PowerShell von Grund auf lernen möchten, werfen Sie doch einmal einen Blick in mein kostenloses Buch über PowerShell Programmierung. Auch das Kapitel über GUI-Programmierung mit PowerShell geht weit (z.B. Baumansichten – treeview, allerdings noch nicht veröffentlicht – nur für Spender ist die neueste Version zum download verfügbar) über das hier hinaus.

Schnelle Powershell Befehle anhand des Beispiels grep

Hier ein Quick&Dirty Beispiel wie man schnelle Powershellscripte schreibt um die eigenen Scripte ordentlich zu tunen. Unter Powershell gibt es zwar mit select-object und where-object als auch select-string einem grep aus dem UNIX- bzw. LINUX-Bereich ähnliche Befehle, doch so wirklich Bash-Gefühle kommen dabei nicht auf. Einen ersten Turbo können Sie einschalten, wenn Sie Parameter wie -inculde, -exclude oder -filter des jeweiligen Befehls verwenden, statt den Befehl alle Informationen beschaffen zu lassen und diese hinterher mit dem where-object auszuwerten. Vielleicht kennen Sie die Möglichkeit eine Funktion zu schreiben. Schneller funktioniert aufgrund des Streaming-Modus jedoch ein Filter. Wenn Sie dasselbe Script noch einmal mit dem Stichwort function statt filter einleiten können Sie das Laufzeitverhalten sehr schön überprüfen indem Sie den Befehl ls c: -recurse | grep suchbegriff aufrufen. Schauen Sie sich auch im Taskmanager ruhig einmal den Speicherverbrauch an!

filter grep ($suchbegriff) {
 $_  | out-string -stream | select-string $suchbegriff
 # Etwas aufwändiger, doch dafür werden die Objekte nicht als String, sondern nach wie vor als Objekt zurück gegeben mit folgender Zeile: # if (@($_  | out-string -stream | select-string $suchbegriff -simplematch).count -gt 0) {$_} }
Bei function wird der Befehl vor der Pipe erst einmal alle Daten (im obigen Beispiel alle Dateinamen) in einen Puffer schreiben. Erst wenn alle Dateien von Lw. C: im Puffer sind wird dieser an die Funktion nach der Pipe übergeben. Das out-string -stream hat damit allerdings nichts zu tun! Durch die Einleitung mit filter nimmt unser grep allerdings schon die Eingaben von ls entgegen sobal der erste Dateiname vorliegt. Die beiden Kommandos arbeiten also parallel. Das führt zu weniger Speicherverbrauch und zu schnelleren Ergebnissen.

Wer das Tuning auf die Spitze treiben möchte greift dennoch wieder zu einer Funktion. Denn filter schreibt auch eine function nur bettet er automatisch den kompletten Code in einen process Block ein. Das Beispiel:

function {
  process{
   # mein code
  }
}

ist also dasselbe wie:

filter {
  # mein code
}

In Ihrer Funktion gibt es aber vielleicht Dinge die nur 1 x zu Beginn und 1 x am Ende durchgeführt werden müssen, während die eigentliche Verarbeitung für alle übergebenen Objekte durchgeführt werden müssen. Bei einem filter würde immer komplett alles durchlaufen werden.

function {
  begin {
   # Hier könnten Sie z. B. den aktuellen Verzeichnispfad, oder andere Ausgangsbedingungen in eine Variable retten bevor Sie zu wüten anfangen
  }
  process {
   # Hier findet die eigentlich Verarbeitung statt. Möglicher Weise benutzen Sie cd um Verzeichnisse zu wechseln etc...
  }
  end {
   # Wechseln Sie wieder in das ursprüngliche Verzeichnis zurück geben Sie Variablen frei und führen Sie weitere "Aufräumvorgänge" durch
  }
}

Der Code bei begin würde also bei der ersten Objektübergabe erfolgen. Für alle weiteren Objekte würde nur noch der Code im Bereich process ausgeführt werden. Nach der Verarbeitung des letzten Objekts würde schließlich der Code unter end ausgeführt werden.

Möchten Sie Ihre Funktion dauerhaft verfügbar haben, sollten Sie diesen in eine Datei namens profile.ps1 schreiben und im Verzeichnis C:\Windows\System32\WindowsPowerShell\v1.0 ablegen, wenn er allen Benutzern des Computers zur Verfügung stehen soll. Wenn es nur für Ihren Benutzer sein soll, dann unter C:\Users\Ihr Benutzername\Documents\WindowsPowerShell.

Wenn Sie Daten von anderen Computern sammeln und auswerten, dann lassen Sie die Auswertung auf den anderen PCs laufen und holen Sie sich nur die Ergebnisdaten ab.

Langsam:

invoke-command {get-process} -computer name1,name2,name3 | where-object {$_.VM -gt 30MB} | sort PSComputerName | select Name,VM

Schnell:

invoke-command {get-process | select Name,VM | where-object {$_.VM -gt 30MB}} -computer name1,name2,name3 | sort PSComputerName

Des Weiteren gibt es für invoke-command den Parameter -throttlelimit. Dieser gibt an wieviele Computer gleichzeitig abgefragt werden. Der Standardwert ist 32. Je nach Aufgabe kann es sich lohnen mehr oder weniger Rechner anzugeben.

Der Befehl measure-command {Ihre Befehle oder Script} gibt Auskunft über die Zeit die Ihr Script zur Ausführung gebraucht hat.

Von der Eingabeaufforderung Parameter an Powershellscripte übergeben

Was passiert, wenn Sie ein Powershellscript doppelklicken? Das ist natürlich davon abhängig, welche Zusatzsoftware (z.B. PowerGui) Sie installiert haben, doch standardmäßig wird unter Windows Server 2008 R2 z. B. der Texteditor Notepad gestartet anstatt das Script auszuführen. Das ist auch noch so eine tolle Schutzmaßnahme, die das Ausführen von „bösen“ (und natürlich auch guten) Powershellscripten erschweren soll. Das ist auch der Grund warum sich keine Parameter an Powershellscripte übergeben lassen. Dieser Registry-Hack beseitigt das Problem:

Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\Shell\open\command]
@="\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" \"-file\" \"%1\" %*"
Wenn Sie mögen können Sie den schräg dargestellten Text einfach in einer Datei mit der Endung .reg abspeichern und doppelklicken. Danach können Sie per Doppelklick die Powershell-Scripte starten und auch Parameter übergeben.

Innerhalb des Powershellscripts können Sie die Parameter entweder über die Variable $args oder $Input abfragen. Bei mehreren Parametern mit $args[0] den 1., $args[1] den 2. etc…