Archive for the ‘Powershell’ Category

Powershell-Skripte mit Dateien aus dem Windows Explorer mittels Drag&Drop versorgen

12 August 2016

Man wundert sich immer wieder, dass unter Windows die Drag&Drop-Methode seltener zum Einsatz kommt als z. B. bei Mac-Benutzern. Während für viele Mac-Benutzer dies zum täglichen Brot gehört, tun sich Windows Benutzer oft damit schwer, wohl auch aus der Erfahrung heraus, dass bestimmte logische Drag&Drop-Kombinationen einfach nicht funktionieren. Woran liegt dies? An den faulen Programmierern! Um dieser Sache einen Riegel vorzuschieben, hier eine Beschreibung wie man Powershell-Skripte Drag&Drop fähig bekommt.

Folgende Ausgangssituation. Man hat ein sinnvolles Powershell-Skript, welches auf bestimmte Dateien angewendet werden soll. Man könnte nun unter Powershell das Skript schreiben und dem Benutzer sagen, er soll die betreffenden Dateien als Arrayparameter übergeben. Der kuckt einen blöd an und sagt: Bahnhof.

Um dem unbedarften Benutzer die Möglichkeit zu bieten bestimmte Powershell-Skripte auf bestimmte Dateien anzuwenden sollte die Sache über den Windows-Explorer funktionieren. D. h. der Benutzer markiert die gewünschten Dateien und zieht – also per Drag&Drop eine oder mehrere auf das Powershell-Skript und lässt sie dort los. Das Skript bekommt die ausgewählten Dateien als Parameter übergeben und kann seiner Arbeit nachgehen.

Von Hause aus ist dies leider in Powershell nicht vorgesehen. Aber mit einem kleinen Trick und Umweg über Batch-Dateien klappt es dann doch. Dazu benennt man am einfachsten die Batch-Datei wie die Datei mit dem Skript, nur anstatt der Endung .PS1 hat man eben .BAT  oder .CMD.

@ECHO OFF
REM Powershellscript.bat
REM
SET PSScript=%~dpn0.ps1
SET PSScriptPath=%~dp0
SET args=’%1′
:More
SHIFT
IF ‚%1‘ == “ GOTO Done
SET args=%args%,’%1′
GOTO More
:Done
REM funktioniert nur, wenn .BAT gleichen Dateinamen wie .PS1-Datei hat:
Powershell.exe -noprofile -noexit -command ". {$count=0; foreach($file in %args%) {. %PSScript% $file; $count++ }; ‚Processed ‚ + $count; }"

Hat man also das Skript MeinPowershellskript.PS1 und möchte dieses mittels Drag&Drop mit Dateien aus dem Windows Explorer befeuern, dann muss auch obige Batchdatei mit dem Namen MeinPowershellskript.BAT im selben Verzeichnis existieren. Der Benutzer zieht die Dateien auf die .BAT-Datei und nicht auf die .PS1-Datei!

Das Powershell-Skript kann dann mittels der immer verfügbaren Variablen $args die übergebene Datei abfragen und darauf reagieren, z. B. so:

"Datei zum Bearbeiten: $args[0]"
Read-Host "Return zum Beenden drücken"

Werden mehrere Parameter übergeben:

$Index=0
foreach($arg in $args) {
  "$(($Index++)): $arg"
}
Read-Host "Return zum Beenden drücken"

Im obigen Beispiel wird das Powershell-Skript für jede zu bearbeitende Datei separat hintereinander aufgerufen. Manchmal kann es dabei sinnvoll sein, den Pfad zu kennen, wo das Skript liegt. Um dies dem Powershell-Skript mitteilen zu können, kann man den Aufruf des Skripts etwas ändern, aus

{. %PSScript% $file; $count++ }

in

{. %PSScript% $file %PSScriptPath%; $count++ }

Warum aber die Skripte einzeln ausführen und nicht alle Dateien auf einen Schlag übergeben? Bei zu vielen Dateien geht die Eingabeaufforderung in die Knie und bringt blöder Fehlermeldungen, deshalb dieser Weg. Etwas langsamer und umständlicher aber dafür zuverlässiger.

Weitere Infos:

http://stackoverflow.com/questions/2819908/drag-and-drop-to-a-powershell-script

Invocation-Context:
http://gallery.technet.microsoft.com/scriptcenter/cb72e4e6-4a68-4a2e-89b7-cc43a860349e#content

Binärtyp einer EXE-Datei unter Windows ermitteln, ob 32- oder 64-Bit–man spricht auch von Bitness

27 Juli 2016

Möchte man unter Windows bei einer ausführbaren Datei wissen, ob diese eine 32-Bit oder 64-Bit Version ist, dann kennt die Win32-API die Funktion GetBinaryType. Mittels Powershell kann man diese Funktion wie folgt nutzen:

$MethodDefinition = @‘

// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364819(v=vs.85).aspx
[DllImport("kernel32.dll")]
public static extern bool GetBinaryType(string lpApplicationName, out uint lpBinaryType);

‚@

$Kernel32 = Add-Type -MemberDefinition $MethodDefinition -Name ‚Kernel32‘ -Namespace ‚Win32‘ -PassThru

# Funktioniert aber nur, wenn man sich in einem 64-Bit Prozess befindet, also: [System.IntPtr]::Size -eq 8
$type=0
$Kernel32::GetBinaryType("c:\windows\system32\cmd.exe", [ref] $type)  # unter 64-Bit sollte es 6 sein
$type
$Kernel32::GetBinaryType("c:\windows\syswow64\cmd.exe", [ref] $type)  # unter 64-Bit sollte es 0 sein
$type

# mögliche Werte für $type:
# SCS_32BIT_BINARY = 0, // A 32-bit Windows-based application
# SCS_64BIT_BINARY = 6, // A 64-bit Windows-based application.
# SCS_DOS_BINARY = 1,   // An MS-DOS – based application
# SCS_OS216_BINARY = 5, // A 16-bit OS/2-based application
# SCS_PIF_BINARY = 3,   // A PIF file that executes an MS-DOS – based application
# SCS_POSIX_BINARY = 4, // A POSIX – based application
# SCS_WOW_BINARY = 2    // A 16-bit Windows-based application

Dabei ist zu beachten, wenn man den Test macht, dass IntPtr 8 liefern sollte, sonst befindet man sich in einem simulierten WOW64-32Bit-Prozess und bekommt verwunderliche Ergebnisse zurück.

Vorsicht mit Get-NetFirewallRule bzw. WMI MSFT_NetFirewallRule-Klasse bei Abfrage der Enabled-Eigenschaft – gilt allgemein bei $True und $False in Powershell

10 Juli 2016

Ein Script welches Windows Firewall Regel-Eigenschaften abfragte funktionierte nicht wie erwartet. Es wurde immer die Enabled Eigenschaft abgefragt aber es kam nicht das erwartete Ergebnis, wurde die Abfrage aber negiert, kam das erwartete Ergebnis heraus! Am Ende stellte es sich heraus, dass die Enabled-Eigenschaften kein klassisches Bool sondern einen String zurückgibt, hier die Story:

Fragen wir zunächst die aktuelle Anzahl von Regeln ab, hier bei einem aktuellen Windows 10:

$r=Get-NetFirewallRule
$r.length
413

Es gibt also 413 Regeln. Nun sollen alle Regeln ermitteln werden, die aktiviert sind:

($r | where enabled -eq $true).length
184

Jetzt sollen alle Regeln ermittelt werden, die nicht aktiviert sind:

($r | where enabled -eq $false).length
0

Die 0 ist jetzt nicht das erwartete Ergebnis, wenn man davon ausgeht, dass es 413 Regeln gibt, davon sind 184 aktiv, dann sollten mehr als 0 inaktiv sein.

Wo liegt das Problem? Das Problem ist, dass bei $True oder $False in Powershell immer True oder False am Bildschirm ausgegeben wird. Man interpretiert einen solchen Wert schnell als Boolean. Aber in diesem Fall hat man es nicht mit einem Boolean zu tun:

$r[0].enabled.pstypenames
Microsoft.PowerShell.Cmdletization.GeneratedTypes.NetSecurity.Enabled
System.Enum
System.ValueType
System.Object

Es ist also ein Aufzählungstyp!

($r | where enabled -eq "True").length
184
($r | where enabled -eq "False").length
229

So macht die Sache mehr Sinn!

Oder man macht die Abfrage über den passenden Typ:

($r| where enabled -eq ([Microsoft.PowerShell.Cmdletization.GeneratedTypes.NetSecurity.Enabled]:
:True)).length
184
($r| where enabled -eq ([Microsoft.PowerShell.Cmdletization.GeneratedTypes.NetSecurity.Enabled]:
:False)).length
229

Übrigens: $true –eq "True" ergibt immer True, auch wenn man $true –eq "irgendwas" setzt! Und $False –eq "False" ergibt immer False, egal mit was verglichen wird, außer $false!

Nützliche Powershellfunktionen zum Hin- und Herkonvertieren in Base64 und Splitten einer Zeichenkette

27 Mai 2016

Ohne viel blabla:

Function ConvertTo-Base64 {

    [CmdletBinding()]
    [OutputType([string])]
    Param (
        [string]$String
    )
    [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($String))

}

Function ConvertFrom-Base64 {
 
    [CmdletBinding()]
    [OutputType([string])]
    Param (
        [string]$String
    )
    [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($String))

}

Function Split-String {

# splittet einen String in Blöcke mit einer bestimmten Länge

    [CmdletBinding()]
    Param (
        [parameter(Mandatory=$true)]
        [string]$String,
        [int]$BlockLength=60
    )
   
    $blocks = [System.Math]::Floor($String.Length / $BlockLength)
    $newString = ""

    for ($line = 0; $line -lt $blocks; $line++) {
        $newString += $String.Substring($line * $BlockLength, $BlockLength) + [System.Environment]::NewLine
    }

    If ($blocks * $BlockLength -lt $String.Length) {
        $newString += $String.Substring($blocks * $BlockLength)
    }

    $newString
}

Damit kann man eine Datei einlesen und in einen Base64-String umwandeln, etwas bearbeiten und wieder zurückwandeln:

$c=get-content -Raw C:\windows\win.ini
$b=ConvertTo-Base64 $c
# Split-String bricht den String nach 60 Zeichen um
$s=Split-String $b
$nc=ConvertFrom-Base64 (Split-String $s)
# Trotz Konvertierung und Splitten ist der konvertierte String identisch
$c -eq $nc

Wider die Arbeitsmoral

28 April 2016

Wenn man mal keinen Bock hat eine Arbeit zu Ende zu führen, warum dann nicht einfach selbstgestrickte Fehlermeldungen erzeugen?

Da sich hier vieles um Powershell dreht, hier ein Zweizeiler, welcher die Möglichkeiten schön darstellt:

Add-Type -AssemblyName System.Windows.Forms

[System.Windows.Forms.MessageBox]::Show("Das Installationsprogramm hat einen Fehler festgestellt: 0xc80003f3","Eigenständiges Windows Update-Installationsprogramm", [System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Error)

Baut man dies in ein Script ein, Profis verschlüsseln die Sache vorab, damit es nicht gar so auffällig ist, dann kann man immer sagen: Geht jetzt nicht, hab ein unlösbares Problem! Muss erst nochmal recherchieren…

Viel Spaß mit der gewonnen Freizeit!

AzureResourceManager und die Hintergründe zu Switch-AzureMode

5 März 2016

Microsofts Azure wird nicht mehr verschwinden. Wer sich effektiv mit Azure beschäftigt, der sollte wie immer per Powershell die Sache angehen. Auf GUIs kann man sich in der Regel nicht verlassen, da diese einer gewissen Mode unterliegen. Aus diesem Grund wird in diesem Blog versucht so gut wie alles per Kommandozeile bzw. Powershell zu regeln.

Blöd nur, wenn sich nun aber die Powershell Cmdlets ebenso ändern und man keinen Plan hat warum, wieso und weshalb. So liest man oft von Switch-AzureMode oder von Azure Resource Manager (ARM). Hier ein Auszug einer Microsoft Dokumentation https://azure.microsoft.com/de-de/documentation/articles/virtual-machines-deploy-rmtemplates-powershell/:

Azure PowerShell ist derzeit in zwei Versionen verfügbar: 1.0 und 0.9.8. Wenn Sie bereits Skripts haben und diese nicht sofort ändern möchten, können Sie weiterhin Version 0.9.8 verwenden. Bei der Verwendung der Version 1.0 sollten Sie Ihre Skripts sorgfältig in Präproduktionsumgebungen testen, bevor Sie sie in der Produktionsumgebung einsetzen, um unerwartete Folgen zu vermeiden.

Cmdlet-Namen der Version 1.0 haben das Muster {Verb}-AzureRm{Nomen}, während Namen der Version 0.9.8 nicht Rm enthalten (z. B. „New-AzureRmResourceGroup“ statt „New-AzureResourceGroup“). Wenn Sie Azure PowerShell 0.9.8 verwenden, müssen Sie zunächst den Ressourcen-Manager-Modus aktivieren, indem Sie den Befehl Switch-AzureMode AzureResourceManager ausführen. Dieser Befehl ist in 1.0 nicht erforderlich.

Das ist alles schön und gut, aber das Web ist voll von alten Beispielen und Code-Snippets und manchmal wäre es ganz gut tiefergehende Infos zum Thema zu erhalten.

Mittlerweile wird auf einen speziellen Artikel verwiesen, der die neuen Möglichkeiten des Ressourcen-Manager-Model anschaulich darlegt: https://azure.microsoft.com/de-de/documentation/articles/resource-manager-deployment-model/

Wenn man zum Thema etwas recherchiert, dann kann man diesen Wiki-Eintrag in Github finden, der geht sehr ausführlich nochmal auf die Hintergrunde ein: https://github.com/Azure/azure-powershell/wiki/Deprecation-of-Switch-AzureMode-in-Azure-PowerShell

Es gibt sogar ein Script, welches automatisch virtuelle Maschinen vom Azure Service Management (ASM) Modell ins Azure Resource Mangement (ARM) Model migriert: https://github.com/fullscale180/asm2arm.

Unicodezeichen in Powershell benutzen bzw. die Suche nach dem Daumen nach oben

8 Februar 2016

Möchte man bestimmte Zeichen in Powershell benutzen, die nicht direkt per Tastatur verfügbar sind, kann man den Datentyp [char] benutzen.

So erhält man z. B. durch Eingabe von

[char]0x2122

das Trademark-Symbol ™. Soweit so gut. Schwieriger wird es aber mit Zeichen, welche nicht direkt als Unicode zur Verfügung stehen.

Da gibt es z. B. den Daumen nach oben. Laut dieser Seite http://www.iemoji.com/view/emoji/56/people/thumbs-up-sign wäre der Code 0xd83ddc4d als UTF-16 also Unicode. Allerdings erhält dabei:

[char]0xd83ddc4d
Der Wert "-667034547" kann nicht in den Typ "System.Char" konvertiert werden. Fehler: "Der Wert für ein Zeichen war zu groß oder
zu klein."
In Zeile:1 Zeichen:1
+ [char]0xd83ddc4d
+ ~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvalidCastIConvertible

Was kann man tun? Nun die Tabelle bei der Webseite bietet noch UTF-32 an. Und UTF-32 kann man so benutzen:

[char]::ConvertFromUtf32(0x1f44d)

und erhält dafür!

Daraus kann man dann Konstrukte basteln wie dieses:

"Ich finde das gut $([char]::ConvertFromUtf32(0x1f44d))"

Was “Ich finde das gut ” ergibt.

Wenn es trotz allem nicht klappen sollte, hilft dieser Artikel noch weiter: https://mnaoumov.wordpress.com/2014/06/14/unicode-literals-in-powershell/

Der Herausgeber der Treibersoftware konnte nicht überprüft werden

31 Januar 2016

Hat mich doch gerade bei Windows 10 ein unverhofftes Update erreicht. Auf einmal steht da ein Fenster mit rotem Rahmen.

[Windows-Sicherheit]
—–
Der Herausgeber der Treibersoftware konnte nicht überprüft werden.

Diese Treibersoftware nicht installieren
Sie sollten auf der Website des betreffenden Herstellers nach aktualisierter Treibersoftware für das Gerät suchen.

Diese Treibersoftware trotzdem installieren
Installieren Sie nur Treibersoftware, die Sie von der Website oder einem Datenträger des Herstellers erhalten haben. Nicht signierte Software aus anderen Quellen kann Schäden auf dem Computer verursachen oder zum Verlust bzw. Diebstahl von Informationen führen.
—–
Details einblenden

Schön so was, da schrillen gleich alle Alarmglocken. Denn ich hatte kein Update angefordert. Hier findet man eine Erklärung, wann so ein Fenster auftaucht, wenn z. B. das Authenticode Zertifikat abgelaufen ist: https://technet.microsoft.com/en-us/windows/ff547534.

Und überhaupt welches Programm bzw. Treiber sollte hier aktualisiert werden?

Man kann nun mittels Process-Explorer von Sysinternals herausfinden, welches Programm da etwas zu installieren versucht aber mit Powershell geht das mit Bordmitteln fast genauso schnell. Mittels

gwmi Win32_Process –Filter "Name=’DrvInst.EXE’"| select Commandline | fl *

wird dann dann die Kommandozeile des Treiberinstallationsprogramms (DrvInst.EXE) abgefragt. Im aktuellen Fall stand da unter anderem etwas von “C:\Program Files\Synaptics\SynFP\…”. Es steht noch mehr dran aber es hat gereicht das betreffende Programm bzw. den Treiber ausfindig zu machen.

In diesem Fall war es der “Synaptics Fingerprint Reader”-Treiber. Sehr dubios, warum will der sich einfach so aktualisieren? Will da jemand meinen Fingerabdruck…

Davon unabhängig ist es ein absolutes Armutszeugnis von Microsoft, dass man immer wieder mit Sicherheitsdialogen gequält wird, die Null Aussagekraft haben und nicht mal dem erfahrenen Benutzer eine Entscheidungsinformation an die Hand geben. Alles sehr traurig.

Icons aus Dateien bzw. Icons zu Dateitypen per Powershell extrahieren

19 Januar 2016

Für diese Aufgabe brauchte man seither spezielle Programme, nun kann man Icons auch per Powershell extrahieren.

Hier ein Script um Icons aus .EXE, .DLL, generell aus Resourcedateien zu extrahieren: https://gallery.technet.microsoft.com/scriptcenter/Export-Icon-from-DLL-and-9d309047

und hier die Variante und von Dateitypen das zugehörige Icon zu erhalten: http://learn-powershell.net/2016/01/18/getting-the-icon-from-a-file-using-powershell/

Mittels Powershell geladene DLL-Dateien aus einem Prozess auslesen

4 Januar 2016

Öffnet man unter Windows die Eingabeaufforderung, so kann man mit TASKLIST.EXE die aktuell ausgeführten Programme bzw. Prozesse auflisten. Bei den Optionen kann man /M angeben, dann werden zu jedem Prozess die geladenen DLL-Dateien angezeigt.

Hier ein Beispiel von Windows 8.1:

tasklist /FI "imagename eq cmd.exe" /M

Abbildname                     PID Module
========================= ======== ============================================
cmd.exe                      15872 ntdll.dll, KERNEL32.DLL, KERNELBASE.dll,
                                   msvcrt.dll, winbrand.dll

Nun ist es wie immer schwierig von einer Textausgabe die Daten weiterzuverarbeiten. Aber es gibt ja Powershell und das äquivalente Kommando in Powershell sieht so aus:

Get-CimInstance -ClassName Win32_Process -Filter "Name=’cmd.exe’" | Get-CimAssociatedInstance -Association Cim_ProcessExecutable | ft name

die Ausgabe sieht dann so aus:

name
—-
c:\windows\system32\cmd.exe
c:\windows\system32\ntdll.dll
c:\windows\system32\kernel32.dll
c:\windows\system32\kernelbase.dll
c:\windows\system32\msvcrt.dll
c:\windows\system32\winbrand.dll

da es aber nun Objekte sind, kann man natürlich auch mehr Infos anfordern:

Get-CimInstance -ClassName Win32_Process -Filter "Name=’cmd.exe’" | Get-CimAssociatedInstance -Association Cim_ProcessExecutable | ft name, version

name                               version
—-                               ——-
c:\windows\system32\cmd.exe        6.3.9600.17415
c:\windows\system32\ntdll.dll      6.3.9600.17736
c:\windows\system32\kernel32.dll   6.3.9600.17415
c:\windows\system32\kernelbase.dll 6.3.9600.17415
c:\windows\system32\msvcrt.dll     7.0.9600.17415
c:\windows\system32\winbrand.dll   6.3.9600.17415

Mit etwas Aufwand kann man sich auch einen Prozessbaum bauen: https://p0w3rsh3ll.wordpress.com/2012/10/12/show-processtree/

Mittels Get-CimAssociatedInstance kann man sowieso noch jede Menge interessante Dinge in Erfahrung bringen, z. B. wer angemeldet ist, welche Partitionen ein Laufwerk enthält usw. http://csharpening.net/?p=876.


Folgen

Erhalte jeden neuen Beitrag in deinen Posteingang.