Blog (9)
Komentarze (978)
Recenzje (1)
@FaUstPowershell to nie tylko konsola, czyli aplikacja graficzna w POSHu

Powershell to nie tylko konsola, czyli aplikacja graficzna w POSHu

Tytułem wstępu

uwaga dużo tekstu, jak chcesz obrazki poszukaj gdzie indziej ;)

Każdy obeznany z obecnymi technologiami spotkał się z Powershellem. Administratorzy Windowsów kochają (albo jeszcze nie znają), admini Linuxów podśmiechują się z politowaniem, a zwykli użytkownicy uważają za czarną magię. Wpis ma na celu pokazanie jak w miarę łatwo i przystępnie można ubrać skrypty w Windowsowe GUI i ułatwić sobie pracę z powłoką wymyśloną przez Microsoft.

Uwaga - jeśli nigdy nie miałeś styczności z POSHem, przestudiuj manuale oraz inne wpisy (np ten stworzony przez bachusa)

Na warsztat weźniemy sobie upierdliwą niedoróbkę Windowsa 10 - zmiana bramy domyślnej przy korzystaniu z VPNa. W poprzednich wersjach wystarczyło zaznaczyć odpowiedni checkbox w ustawieniach połączenia i na tym koniec. W 10 wersji okienek już tego nie uświadczymy. Aby dokonać zmiany należy wydać polecenie:

Set-VPNCnnection -name {nazwa_polaczenia} -SplitTunelling {$true/$false}

gdzie $true oznacza separację ruchu czyli używanie bramy z połączenia lokalnego, a $false wyłączy separację i cały ruch będzie przebiegał przez bramę zdefiniowaną w VPNie.

Łatwo co? Gorzej jeśli posiadamy kilka połączeń którymi trzeba żonglować i często zmieniać ich ustawienia, lub z VPNa korzysta nieobeznana osoba.

W tym właśnie celu przygotujemy prostą aplikację z GUI.

Tworzymy interfejs

Interfejs można zaprogramować, jak w każdym środowisku w czystym kodzie, ale (zwłaszcza dla niewprawionych osób) jest to mordęga i okazja do przypomnienia sobie wszystkich znanych przekleństw. Istnieje kilka rozwiązań WYSWIG dla Powershella. Podczas tworzenia wpisu skorzystałem z Admin Script Editor który jest darmowy i do tworzenia interfejsu oraz kodu wystarczy. Istnieją aplikacje komercyjne, takie jak PrimalForms które potrafią o wiele więcej, lecz kosztują też odpowiednio więcej.

Główne okno ASE po stworzeniu projektu wygląda następująco:

590852

Możemy przejść do tworzenia interfejsu (Tool -> ScriptForm Designer). Każdy kto miał do czynienia ze środowiskami programistycznymi typu Visual Studio szybko się tutaj odnajdzie.

Tworzymy takie oto okienko:

590855

Pododawajmy akcje do przycisków, comboboxa oraz ustawmy atrybuty okna:

590857
590858
590859

Gdy już wszystko mamy gotowe wciskamy CTRL+E i przechodzimy do edytora tekstowego gdzie mamy gotowy kod. Kod interfejsu wygląda następująco:


#region ScriptForm Designer

#region Constructor

[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

#endregion

#region Post-Constructor Custom Code

#endregion

#region Form Creation
#Warning: It is recommended that changes inside this region be handled using the ScriptForm Designer.
#When working with the ScriptForm designer this region and any changes within may be overwritten.
#~~< Form1 >~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$Form1 = New-Object System.Windows.Forms.Form
$Form1.AutoSize = $true
$Form1.ClientSize = New-Object System.Drawing.Size(292, 104)
$Form1.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
$Form1.MaximizeBox = $false
$Form1.Text = "Set default VPN gateway"
#~~< btnSplitDisable >~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$btnSplitDisable = New-Object System.Windows.Forms.Button
$btnSplitDisable.Enabled = $false
$btnSplitDisable.Location = New-Object System.Drawing.Point(160, 64)
$btnSplitDisable.Size = New-Object System.Drawing.Size(112, 23)
$btnSplitDisable.TabIndex = 4
$btnSplitDisable.Text = "ENABLE"
$btnSplitDisable.UseVisualStyleBackColor = $true
$btnSplitDisable.add_Click({btnSplitDisable_click($btnSplitDisable)})
#~~< btnSplitEnable >~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$btnSplitEnable = New-Object System.Windows.Forms.Button
$btnSplitEnable.Enabled = $false
$btnSplitEnable.Location = New-Object System.Drawing.Point(16, 64)
$btnSplitEnable.Size = New-Object System.Drawing.Size(112, 23)
$btnSplitEnable.TabIndex = 3
$btnSplitEnable.Text = "DISABLE"
$btnSplitEnable.UseVisualStyleBackColor = $true
$btnSplitEnable.add_Click({btnSplitEnable_click($btnSplitEnable)})
#~~< lblSplitTunellingStatus >~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$lblSplitTunellingStatus = New-Object System.Windows.Forms.Label
$lblSplitTunellingStatus.Location = New-Object System.Drawing.Point(16, 32)
$lblSplitTunellingStatus.Size = New-Object System.Drawing.Size(256, 23)
$lblSplitTunellingStatus.TabIndex = 2
$lblSplitTunellingStatus.Text = ""
$lblSplitTunellingStatus.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
#~~< Label1 >~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$Label1 = New-Object System.Windows.Forms.Label
$Label1.Location = New-Object System.Drawing.Point(10, 8)
$Label1.Size = New-Object System.Drawing.Size(64, 23)
$Label1.TabIndex = 1
$Label1.Text = "Select vpn:"
$Label1.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
#~~< cbVPNName >~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$cbVPNName = New-Object System.Windows.Forms.ComboBox
$cbVPNName.FormattingEnabled = $true
$cbVPNName.Location = New-Object System.Drawing.Point(74, 8)
$cbVPNName.SelectedIndex = -1
$cbVPNName.Size = New-Object System.Drawing.Size(200, 21)
$cbVPNName.TabIndex = 0
$cbVPNName.Text = ""
$cbVPNName.add_SelectedIndexChanged({cbVPNName_changed($cbVPNName)})
$Form1.Controls.Add($btnSplitDisable)
$Form1.Controls.Add($btnSplitEnable)
$Form1.Controls.Add($lblSplitTunellingStatus)
$Form1.Controls.Add($Label1)
$Form1.Controls.Add($cbVPNName)

#endregion

Jak widać wszystko jest czytelne i ładnie opisane. Linie


[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

informują interpreter że ma załadować biblioteki Windows.Forms oraz Drawing które odpowiadają za obsługę okienek w POSHu.

Funkcje

Interfejs już mamy to go oprogramujmy. Poniżej podanego kodu (sekcja custom code stworzona przez ASE) dodajmy:


$VPN = Get-VpnConnection | select name
foreach ($vpnname in $VPN)
{
	$cbVPNName.Items.Add($vpnname.name)
}

Kod ma za zadanie pobrać do zmiennej $VPN nazwy wszystkich połączeń zawartych w systemie i za pomocą pętli dodać wszystkie nazwy do listy wyboru

Ważne aby powyższa pętla znajdowała się w tym miejscu, czyli przed uruchomieniem formularza. Za uruchomienie formatki odpowiada funkcja Main którą znajdziemy poniżej.

Pozostaje nam oprogramować dodane przy tworzeniu formularza zdarzenia. Na pierwszy ogień kod który uruchomi się przy zmianie danych w liście wyboru (wybranie nazwy z listy, bądź wpisanie innego tekstu) :


function cbVPNName_changed($object){
	try
	{
		$selectedVPN = $cbVPNName.SelectedItem
		$status = Get-VPNConnection -name $selectedVPN
		if ($status.SplitTunneling -eq $true)
		{
			$lblSplitTunellingStatus.Text = "VPN isn't a default gateway"
			$btnSplitEnable.Enabled = $false
			$btnSplitDisable.Enabled = $true
		}
		elseif ($status.SplitTunneling -eq $false)
		{
			$lblSplitTunellingStatus.Text = "VPN is a default gateway"
			$btnSplitEnable.Enabled = $true
			$btnSplitDisable.Enabled = $false
		}
	}
	catch
	{
		[System.Windows.Forms.MessageBox]::Show("VPN connection do not exist", "Error", "OK", "Error")
	}
}

Kod ma za zadanie pobrać nazwę połączenia VPN z listy, sprawdzić czy dla połączenia jest włączony SplitTunelling, wyświetlić odpowiedni status i odpowiedni włączyć/wyłączyć przyciski.

2 pozostałe funkcje odpowiadają za obsługę przycisków ustawiających właściwości połączenia:


function btnSplitDisable_click( $object ){
	try
	{
		$selectedVPN = $cbVPNName.SelectedItem
		set-VpnConnection -name $selectedVPN -SplitTunneling $false
		$lblSplitTunellingStatus.Text = "VPN is a default gateway"
		$btnSplitDisable.Enabled = $false
		$btnSplitEnable.Enabled = $true
	}
	catch
	{
		[System.Windows.Forms.MessageBox]::Show("VPN connection do not exist", "Error", "OK", "Error")
	}
	
}

function btnSplitEnable_click( $object ){
	try
	{
		$selectedVPN = $cbVPNName.SelectedItem
		set-VpnConnection -name $selectedVPN -SplitTunneling $true
		$lblSplitTunellingStatus.Text = "VPN isn't a default gateway"
		$btnSplitEnable.Enabled = $false
		$btnSplitDisable.Enabled = $true
	}
	catch
	{
		[System.Windows.Forms.MessageBox]::Show("VPN connection do not exist", "Error", "OK", "Error")
	}
		
}

Kod obu przycisków jest podobny więc przedstawię schemat działania obydwu:

  • przypisz do zmiennej $selectedVPN nazwę połączenia VPN,
  • ustaw status połączenia którego nazwę zapisaliśmy w zmiennej $selectedVPN
  • ustaw odpowiedni status w polu etykiety
  • wyłącz/włącz dany przycisk

W kodzie zdarzeń użyłem prostej obsługi błędów, czyli konstukcji try, catch. W bloku try znajduje się kod służący do obsługi programu, natomiast catch zostaje wyzwolone w momencie gdy pojawi się błąd (niepoprawne dane, zły format etc). W powyższym przypadku otrzymamy komunikat że połączenie o nazwie jaką wpisaliśmy do Combo Boxa nie istnieje w systemie. Możemy zamiast tego przekierować błąd do dziennika, wywołać inny skrypt itp. Cała skrypt znajduje się tutaj. Zrób z nim co chcesz, ale jeśli wykorzystasz kod w innym dostępnym publicznie rozwiązaniu wklej link w komentarzu :)

Tak w skrócie wygląda tworzenie okienkowej aplikacji w Powershellu. Możliwości jego wykorzystania jest sporo - możemy np ułatwić sobie administrację AD, MS Exchange, czy też VMWare'e, ogranicza nas tylko wiedza i możliwości poszczególnych funkcji PSa. Jeśli przebrnąłeś przez ścianę tekstu gratuluję, jeśli nie spróbuj ponownie ;)

Wybrane dla Ciebie
Komentarze (30)