Archiv der Kategorie: Tipps

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 Tipps für Zwischendurch: Kopfzeile und restliche Zeilen einer Textdatei mit einem Befehl getrennt einlesen

Bei einer Textdatei sollen Kopfzeile und die restlichen Zeilen getrennt eingelesen werden. Aber mit einem einzigen Befehl.

Mit folgender Konstruktion ist mir das gelungen.

Zunächst ein paar Testdaten als Here-String:

Dann der Befehl, der die Textzeilen in eine erste Zeile und die restlichen Zeilen aufsplittet und das Ergebnis zwei Variablen zuweist:

Warum verwende ich kein ForEach-Object? In erster Linie weil ich es etwas „übertrieben“ finde, wenn ein Wiederholungsbefehl für einen einzigen Verarbeitungschritt verwendet werden muss. Auch wollte ich keine Variable verwenden. Dann wäre auch Folgendes möglich:

Die Variable Zeilen wird gebraucht, da ich nur so an die Länge des Array beim Zugriff auf dasselbige herankomme.

Es soll direkt nach dem Schema Textzeilen | Verarbeitung => Ergebnis funktionieren. Ohne Zwischenschritte und Variablen.

Was nicht jeder wissen dürfte, auch eim harmloser Scriptblock kann in einen Begin-, Process- und End-Block unterteilt werden. Möchte man in einen Scriptblock etwas pipen, geht das nur mit eimem vorangestellten &, das den Scriptblock ausführt.

Tja, was soll man davon halten? Es funktioniert, ok. Ist das Ganze intuitiv nachvollziehbar? Eher weniger. Auf der anderen Seite, im Vergleich zu einem Befehl in Bash oder Perl, der zum selben Resultat führt, ist die PowerShell-Variante regelrecht leicht verdaulich, denn es kommen weder reguläre Ausdrücke noch kryptische Variablen wie %%S vor;)

Tipps für Zwischendurch: COM-Methoden mit Enum-Werten aufrufen

Dieser Tipp ist etwas spezieller.

Ausgangspunkt ist eine COM-Dll (früher OLE Server oder COM Server genannt), die eine Methode Calculate anbietet, die drei Parameter besitzt. Der erste Parameter ist vom Typ eines Enum mit dem Namen „OpType“, die anderen beiden Parameter sind vom Typ int.

Das Anlegen eines COM-Objekts mit der ProgId ist einfach:

Der Aufruf von Calculate funktioniert zunächst nur, wenn für den Enum-Wert eine Zahl übergeben, z.B. eine 0.

Der Enum OpType ist zunächst nicht bekannt. Ein „Trick“ besteht darin, ihn per Enum-Befehl nachzubauen, den es ab Version 5.0 gibt.

Damit klappt auch der folgende Aufruf:

Tipps für Zwischendurch: Registry-Suche – besser ohne PowerShell

Das Durchsuchen der Registry ist nicht gerade die Stärke der PowerShell. Einfacher geht es mit dem reg-Kommando, dessen Syntax zudem übersichtlich per /? abrufbar ist.

Hier eine Kostprobe. Gesucht sind alle Einträge, die ein „MathLibV1“ enthalten.

Per PowerShell wäre eine solche Suche deutlich umständlicher und vor allem langsamer. Das beginnt bereits mit dem Umstand, dass es keinen Befehl gibt, der die Registry nach Werten durchsucht. Das mit Version 5.0 dazu gekommende Get-ItemPropertyValue-Cmdlet klingt praktisch, ist aber sehr limitiert, da der Name-Parameter keine Platzhalter akzeptiert.

Geht es nur um eine schnelle Suche, ist reg einfach unschlagbar.

Kleine Tipps für Zwischendurch: Namespaces mit using abkürzen

Ich war sehr überrascht als ich in einem Diskussionsthread zu PowerShell Core folgende Zeile fand:

Sollte es wie bei C# auch bei der PowerShell einen using-Befehl geben mit dem sich Namespacenamen abkürzen lassen? Gibt es diese praktische Abkürzung auch bei der Version 5.1.

Die Antwort ist ja, Namespaces lassen sich tatsächlich auf diese Weise abkürzen was gerade größere Skripte mit vielen Typenangaben deutlich übersichtlicher machen wird.

Dazu noch ein Beispiel.

Nicht spektakuär, aber auf alle Fälle eine praktische Erweiterung. Ich frage mich nur, wie ich sie bislang habe übersehen können.

Tipp: Oracle Datenbanken per PowerShell ansprechen

Wer Oracle hört, denkt wahrscheinlich zunächst an hochpreisige Produkte und komplizierte Installationen. Das ist natürlich nur bedingt der Fall.

In jedem Fall ist der Zugriff auf eine Oracle DB per PowerShell sehr einfach. Voraussetzung ist, dass ODP.NET (z.B. als Teil der Developer Tools for Visual Studio) installiert wurde. Die Developer Tools installiert man auch dann, wenn kein Visual Studio vorhanden ist. In jedem Fall gibt es danach eine Assembly mit dem Namen Oracle.ManagedDataAccess.dll in dem Verzeichnis, in das die Developer Tools installiert wurden.

Das folgende kleine Beispiel geht von einer vorhandenen Datenbank aus. Benutzername und Kennwort sind natürlich frei erfunden. Wichtig ist nur, dass der Pfad der Dll-Datei stimmt.

Für eine konkrete Datenbankabfrage muss lediglich ein OracleCommand-Objekt angelegt, der CommandText-Eigenschaft ein SQL-Kommando zugeordnet und per ExecuteReader() ein DataReader geholt werden, der dann per Read() durchlaufen wird. Sollte alles kein Problem sein.

PS: Ein Listener muss dazu nicht mehr konfiguriert werden. Die Oracle DB läuft ohnehin nicht auf dem Computer, auf dem die Abfrage ausgeführt wird.

Wenn das Laden einer Assembly per Add-Type mit der Fehlermeldung „Operation not supported“ fehlschlägt…

Das Laden einer Assembly in einem PowerShell-Skript ist im Grunde sehr einfach: Per Add-Type und dem Path-Parameter.

Vor kurzem hatte ich einen Fall, bei dem sich eine Assembly (Npgsql.dll für den Zugriff auf PostgreSQL-Datenbanken) einfach nicht laden lies. Es war frustrierend, da mir wieder einmal klar wurde wieviel (Lebens-)zeit ich schon mit solchem Sch… verbracht hatte (sorry;).

Am Ende war die Lösung so trivial, dass ich einen Tritt in den Hintern verdient gehabt hätte. Ich hatte vergessen, die Datei zu „entsperren“ also den Zone.Identifier-ADS aus der Datei zu entfernen.

Nach einem Neustart der ISE lies sie sich problemlos laden. Ich hätte vielleicht auch eher nach dem Fehlercode 0x80131515 googlen sollen. Dann wäre ich auf einen StackOverflow-Beitrag gestoßen, der alles erklärt:

https://stackoverflow.com/questions/13799260/powershell-runtime-exception-could-not-load-file-or-assembly

„Suchet und ihr werdet finden“ (Matthäus 7,7)

Kleine Tipps für Zwischendurch: Hex-Zahlen als Dezimalzahlen ausgeben

Das Umwandeln eines Hex-Wertes in einen Dezimalwert ist wirklich sehr einfach:

„Hex“ ist aber keine „magische Zutat“, sondern die wie immer sehr praktische Eigenschaft der PowerShell, das die Felder einer Konstantenliste nicht nur nicht vollständig qualifiziert werden müssen, sondern auch abgekürzt werden können. Offiziell wäre der Name System.Globalization.NumberStyles.HexNumber.

Kleine Tipps für Zwischendurch: Textabfragen mit Sls (Select-String)

Der Umstand, dass bei der PowerShell alles auf Objekten basiert, bietet nicht nur Vorteile. Ein Ausnahme von der Regel ist das Durchsuchen der History.

Wer

eintippt erwartet, dass alle Befehlszeilen ausgeben, die das Wort „startapp“ enthalten. Dem ist aber nicht so, da für das gewünschte Resultat explizit die CommandLine-Eigenschaft abgefragt werden müsste.

Auch ein

bringt nicht. Wer es mit ? alias Where-Object

eingeben. Das ist für das mal eben-Abfragen der History natürlich keine Option. Zum Glück gibt es mit Select-String und seinem Alias sls eine einfache Alternative:

Dieser Befehl durchsucht den Output von Get-History unabhängig von den Namen einzelner Eigenschaften nach dem Wort „startapp“.

Dass bei dieser Abfrage MatchInfo-Objekte resultieren, was eventuell eine Weiterverarbeitung etwas erschweren könnte, wollen wir großzügig übersehen.

Kleine Tipps für Zwischendurch: C#-Code testen

Wer lediglich ein paar Zeilen C#-Code testen möchte, muss dafür nicht Visual Studio starten, um dort ein Projekt anlegen zu müssen, wenngleich dies natürlich kein allzu großer Aufwand ist und es mit Visual Studio Code einen Editor gibt, der vielsprachig ist. Es geht auch per PowerShell und dem vielseitigen Add-Type-Cmdlet. Über den MemberDefinition-Parameter wird der C#-Code für eine Membermethode übergeben. Name der Klasse und eventuell auch ein Namespace werden per Parameter festgelegt.

Das folgende Beispiel veranschauhlicht die einfache Vorgehensweise. Eventuell wäre eine statische Methode noch etwas kürzer.