Archiv der Kategorie: Experten

Convert a hashtable into its string representation

Recently I had to find a technique to update a module manitest file (psd1). Using the Update-ModuleManifest cmdlet is the official way to achieve this. Although using this command is really simple it completely overwrites the current psd1 file and inserts all the „junk“ that I want to keep out from that file.

Another approach is to load the psd1 file with the Import-LocalizedData cmdlet which results to a hashtable.

Updating single entries is easy:

But how do you turn the the whole hashtable with all its keys and values into a string again? Not so easy. Luckily I found an old blog entry by June Blender (when she was still working for Sapien Technologies):

https://www.sapien.com/blog/2014/10/21/a-better-tostring-method-for-hash-tables/

But June restricted her function to simple hashtables with no values that are hashtables themselves.

So I have extend her function so that it works also with hashtable that contains other hashtable values which is necessary to convert a hashtable that represents a psd1 file. Ny function calls itself recursively which which is not hard to accomplish in PowerShell. The difficult part can be to find an exit condition or threat the last value differently. For this reason I am not using foreach but the for command with an index variable to enumerate all the values. For the last value I add „}“ to complete the hashtable string.

As PowerShell expert Kirk Munro suggested in his comments to Junes blog post converting the hashtable to json might be a simpler solution. I have not tried this yet although it probably could have saved me a lot of typing and trying. But hey, why choosing a simple solution if you can spend a couple of hours fiddling with Powershell script code;)

So this should work. I have tested the function with a specific psd1 file. If not please leave a comment or send me a mail.

Tipps für Zwischendurch: Fenster auflisten mit der EnumWindows-API-Funktion

Vor kurzem fand ich nach der Suche nach einer Lösung auf Stackoverflow einen Link auf ein PowerShell-Skript, das ich vor vielen Jahren erstellt hatte. Leider hatte der Autor meinen Namen nicht erwähnt, sonst wäre ich bei Stackoverflow zu unverhofftem Ruhm gekommen. Außerdem verwies der Autor des Beitrags auf meinen alten Blog powershell-knowhow.de, den es schon länger nicht mehr gibt. Da ich das Skript von damals auf diversen Festplatten nicht wiederfinden konnte, musste ich mich noch einmal an die Arbeit machen. Wie sooft war das Ergebnis besser als das Original, da es deutlich kürzer geworden ist. Das ist generell ein Erfahrungswert, den ich aus über 10 Jahren PowerShell ziehen kann, und der vermutlich universell gilt. Um auf die kürzeste Lösung zu kommen, muss man sich in den Bereich, in dem die Lösung gesucht wird, bereits ziemlich gut auskennen.

Wie dem auch sei, die folgende Function Get-WindowData gibt Fensterhandle und Text zu jedem Top-Window-Fenster aus (und das sind bereits erstaunlich viele). Theoretisch lassen sich durch eine Art rekursiven Aufruf natürlich auch die Kindfenster eines Fensters auflisten, aber diese Mühe habe ich mir dann doch nicht gemacht.

Interesant und lehrreich an dem Beispiel ist die Leichtigkeit, mit der sich ein „Callback“, also eine Adresse auf eine Funktion, die von der API-Funktion pro Fenster aufgerufen wird, von PowerShell an eine Win32-API-Funktion übergeben lässt. Merkregel: Überall, wo ein Delegate erwartet wird, darf auch ein Scriptblock übergeben werden.

Kleine Besonderheiten beim Umgang mit einer OrderedDictionary-Collection und wie man sie löst

Nach mehr als 10 Jahren intensiver PowerShell-Nutzung komme ich immer öfter in Situationen, in die Art und Weise wie PowerShell mit (Daten-) Typen umgeht einiges an Nerven kostet und ich mehr und mehr frage, ob dies bei richtigen Skriptsprachen wie Python oder Ruby auch so ist. Ein „Fehler“ aus heutiger Sicht war sicherlich, dass das PowerShell-Team damals im Jahr 2004 das Typensystem des .NET Framework übernommen hatte, das damals selber noch ganz neu war und erst in der Version 1.1 vorlag. Aus meiner Sicht wäre es besser gewesen, wenn die PowerShell-Entwickler damals ein eigenes Typensystem implementiert und sich nur so wenig wie möglich an das .NET-Typensystem angedockt hätten. Aber diese Option gab es damals nicht.

Die „Besonderheit“, um die es in diesem Blog-Beitrag geht, wird nur auf den zweiten Blick deutlich. Ausgangspunkt ist eine Hashtable, also eine Liste von Schlüssel=Wert-Paaren. Hier ein Beispiel:

Bis zu diesem Punkt ist hoffentlich alles klar. Wir haben eine Hashtable mit drei Schlüssel-Wert-Paaren. Die Schlüssel sind „1000“, „2000“ und „3000“.

Ein Problem mit Hashtables ist, dass es keine Garantie gibt, dass die Elemente in der Folge ausgegeben werden, in der sie hinzugefügt wurden. Um dieses Problem zu umgehen, muss dem @{} nur ein [Ordered] vorangestellt werden.

Jetzt bleibt die Reihenfolge erhalten. Der Grund dafür ist ganz einfach, dass die Hashtable keine Hashtable mehr ist. Die Variable H steht jetzt für ein OrderedDictionary. Das bedeutet unter anderem, dass die Methode ContainsKey für das Abfragen eines Schlüsselwertes nicht mehr vorhanden ist. Ein Skript, dass diese Methode verwendet, muss daher angepasst werden.

Sehr viel schwerer wiegt, dass die Verwendung des Schlüsselwertes nicht mehr funktioniert.

Ein

führt zu keinem Ergebnis mehr. Ein Wert wird über die allgemeine Item()-Methode abgerufen. Aber auch hier gibt es eine „Besonderheit“. Damit der Wert als Schlüssel und nicht als Index erkannt wird, muss der vom Typ Object sein.

Ein

liefert wieder nichts. Ein

liefert den Wert „Wert 1“.

Bei solchen Klimmzügen frage ich immer, wenn ich mit meinen 10+ Jahren PowerShell-Know how ins Schwimmen komme und einiges probieren muss, um zu gewünschten Ergebnis zu kommen (allerdings komplett ohne Internet-Recherche), wie soll es ein typischer PowerShell-Anwender ohne Programmiererfahrung schaffen?

Die Lösung für die ganze „Hashtable-Problematik“ besteht für mich darin, auf das [Ordered] zu verzichten und damit zu leben, dass die Reihenfolge der Werte nicht der Reihenfolge entspricht, in der die Werte zur Hashtable hinzugefügt wurden.

PS: Ein kleiner Trick fiel mir beim Schreiben des letzten Absatzes ein: Per Typenkonvertierung in eine Hashtable verhält sich das OrderedDictionary wieder wie eine Hashtable und es gibt z.B. eine ContainsKey()-Methode.

Damit funktioniert auch der Zugriff über den Schlüssel wieder wie gewohnt:

PowerShell-Experten-Tipp: Array-Eigenschaften in Klassen

Eigenschaften in per class-Befehl definierten Klassen können einen beliebigen Datentyp besitzen, natürlich auch Array bzw. den Datentyp für ein mehrdimensionales Array mit Werten eines wiederum beliebigen Typs. Die Schreibweise für den Datentyp ist dieselbe wie bei einer Variablendeklaration, also z.B. [Double[,]] für ein zweidimensionales Array mit Double-Werten.

Das folgende Beispiel ist abstrakt und dient lediglich dazu, die Syntax zu veranschaulichen.

Ausgangspunkt ist eine Klasse M. Sie besitzt eine Property Prop1.

Die zweite Klasse N> besitzt eine Array-Property mit Objekten vom Typ N.

Der Eigenschaft Werte werden direkte Werte zugewiesen, das Array muss also nicht initialisiert werden.

Soll eine Klasse eine Eigenschaft besitzen, die auf einem zweidimensionalen Array basiert, wird für Schreibweise des Datentyps die bei der PowerShell allgemein übliche Schreibweise verwendet.

Außerdem muss die Property explizit per New-Object initialisiert werden, da ansonsten das Array nicht angelegt wird.

Tipp: Die Anzahl der Dimensionen liefert die rank-Eigenschaft des Array. Über die Methode GetUpperBound() erhält man die Obergrenze für eine bestimmte Dimension (0, 1 usw.).