Windows PowerShell jest oczywiście powłoką tekstową z wierszem poleceń. Wszystkie więc czynności, jakie możemy w nim wykonać, wiążą się z wpisywaniem tychże poleceń i odczytywaniem wyników, podawanych także w postaci tekstowej. Prosto, minimalistycznie i naturalnie.
I właśnie dlatego jednym z pierwszym eksperymentów, jakie przy użyciu tego narzędzia wykonałem, była… próba wyświetlenia okienka z GUI :) Najpierw miało to być skromne okienko komunikatu (message box), jednak szybko okazało, że nie będzie to wcale łatwe. Domyślnie w PowerShellu nie jest bowiem załadowane assembly System.Windows.Forms
, które to jest niezbędnie potrzebne do zabawy z okienkami. Nie oznacza to na szczęście, że oprócz jego załadowania należy też korzystać z niewygodnego interfejsu refleksji, aby cokolwiek przy jego użyciu zrobić (np. stworzyć obiekt klasy i wywołać jego metodę). Wystarczy po prostu jedno wywołanie w stylu:
i od tej pory można już zupełnie normalnie korzystać z Windows.Forms… No, prawie. Możemy w każdym razie uraczyć się okienkiem komunikatu:
A gdy już na nie popatrzymy, to pewnie zechcemy też czegoś więcej – na przykład “prawdziwego” okna, czyli zwykłej formy. To także jest do zrobienia:
Bez większego problemu można by zresztą dodać do formy kontrolki potomne, jak przyciski czy pola tekstowe… Jest jednak jeden poważny problem: właściwie nie ma sposobu na podpięcie pod nasze kontrolki procedur zdarzeniowych – nawet mimo tego, że przy użyciu wyjątkowo perfidnej sztuczki da się w PowerShellu tworzyć delegaty. Stworzone okienka pozostaną więc na wpółmartwe i specjalną funkcjonalnością nas nie zaskoczą.
Zasadniczo to systemy uniksowe znane są z intensywnego wykorzystania trybu tekstowego, czyli wiersza poleceń konsoli. W przypadku systemu Windows wydaje się, że jest to bardziej pozostałość po starym (nie)dobrym DOS-ie, która została prawie całkowicie zastąpiona przez interfejs graficzny. Problem jednak w tym, że nie wszystko da się zawsze wygodnie wyklikać – a co więcej, takie czynności niezbyt nadają się do zautomatyzowania (na przykład po to, aby wielokrotnie przeprowadzać zmiany w konfiguracji na wielu maszynach). Mając elastyczny tryb tekstowy, zwykle bowiem wystarczy napisać odpowiedni skrypt; w przypadku trybu graficznego nie ma takiej możliwości.
Ten mankament był w Windows obecny od dawna i różne były sposoby na jego obejście – począwszy od plików wsadowych (.bat) przez eksporty/importy Rejestru (.reg), pliki .inf czy skrypty WSH (Windows Scripting Host). Te ostatnie są na przykład znane z powodu… kilku wirusów, które zdołały się szeroko rozprzestrzenić, wykorzystując tę technologię (np. słynny I.Love.You).
Wszystkie podobne pomysły rozwiązywały jednak cały problem dość średnio. Lecz od jakiegoś czasu istnieje Windows PowerShell, który wydaje się znacznie ciekawszym rozwiązaniem. Jest to tekstowa powłoka poleceń – wraz ze skryptowym językiem programowania – która nie tylko jest o wiele bardziej przydatna niż wcześniejsze wynalazki Microsoftu, ale też wypada wcale nieźle w porównaniu z innymi shellami, jak choćby bash. Ma ona bowiem kilka interesujących cech szczególnych:
W tym momencie zapewne przydałby się wymowny przykład, który może wyglądać choćby następująco:
To polecenie najpierw listuje wszystkie procesy w systemie (ps
), następnie wybiera spośród nich te, które nie odpowiadają, aby w końcu posłać je do Krainy Wiecznych Zwisów :) Operujemy tutaj na obiektach reprezentujących procesy, a jedną z ich właściwości jest, jak widać, Responding
, którą można użyć do filtrowania.
W pełnej wersji komenda wyglądałaby tak naprawdę następująco:
co być może wygląda wymowniej, lecz jest z pewnością bardziej rozwlekłe. Na szczęście PowerShell definiuje fabrycznie kilkanaście aliasów, które mapują się na polecenia odpowiadające z grubsza tym znanym z innych shelli – jak dir
, ls
, cd
, rm
, ps
, man
czy type
. To pozwala w miarę szybko poznać podstawowe komendy i przyspiesza ich wpisywanie.
Na koniec trzeba stwierdzić, że przydatność PowerShella zależy prawdopodobnie przede wszystkim od tego, czy potrafimy wykorzystać całe to ogromne bogactwo klas .NET, COM i WMI, które powłoka ta udostępnia. Jeśli tak, to możemy znaleźć dla niej szerokie pole zastosowań. Polecam w każdym razie przyjrzenie się temu wynalazkowi (dostępnemu na każdą sensowną wersję Windows, tj. od XP SP2 wzwyż). Sam lubię używać go do zabijania procesów znacznie bardziej niż standardowego Menedżera zadań ;-)
Dawno, dawno temu miałem dość oryginalny pomysł na program użytkowy. Miała to być (o wiele) lepsza wersja konsoli Windows, której wyższość miała się objawiać w wyglądzie oraz możliwościach; pewne inspiracje czerpałem tutaj z terminali linuksowych oraz basha (nie, nie chodzi tu o serwis z cytatami z IRC-a ;P). Jak większość “genialnych” pomysłów, także i ten nie doczekał się realizacji. Przyczyną było to, iż nie za bardzo wówczas wiedziałem, jak można zapewnić, aby aplikacje konsolowe funkcjonowały w ramach projektowanego terminala tak, jak to robią w domyślnym oknie konsoli w Windows. Większość problemu rozbijała się o to, jak pobierać dane przez ów program produkowane i jak przekazywać do niego te wpisywane przez użytkownika.
Konsolka mała i ciasna, ale własna
Jako że było to bardzo dawno temu, nie znałem wtedy jeszcze takich terminów jak ‘standardowe wejście/wyjście’, ‘proces potomny’ czy ‘pipe‘, które byłyby tutaj bardzo pomocne. Dowiedziałem się o nich więcej dopiero znacznie później, przy czym wiedza ta w większości odnosiła się do systemu zgoła innego niż Windows :)
Pomyślałem jednak, że do problemu można by wrócić – zwłaszcza, że pytanie o to, jak uruchomić program z przekierowanym wejściem i wyjściem, pojawia się stosunkowo często. Okazało się, że nie jest to specjalnie trudne i sprowadza się właściwie do trzech kroków – z których tylko pierwszy może być nieco zakręcony. Cała procedura (w Windows) może wyglądać na przykład tak:
CreatePipe
), które posłużą nam do odbierania wyjścia od i dostarczania wejścia do procesu potomnego. Należy przy tym zwrócić uwagę na dwie rzeczy:
SECURITY_ATTRIBUTES
(tak, tej którą w 95% przypadków się ignoruje) i przekazując ją do funkcji tworzącej pipe.CreateProcess
). Musimy mu podać właściwe końce rurek w strukturze STARTUPINFO
oraz poinstruować Windows, by były one wykorzystywane (flaga STARTF_USESTDHANDLES
). Ponadto musimy wskazać, że chcemy dziedziczyć uchwyty i że utworzony proces konsolowy nie powinien pokazywać okienka (CREATE_NO_WINDOW
) – to już przekazujemy w parametrach CreateProcess
.WaitForMultipleObjects
. Natomiast organizacja wejścia zależy już od naszej aplikacji. Warto pamiętać, że w standardowej konsoli jest ono buforowane wierszami, zatem i my powinniśmy wysyłać coś do procesu potomnego dopiero wtedy, gdy zbierzemy całą linijkę danych wejściowych.Łatwe, prawda? ;-) W innych systemach operacyjnych robi się to minimalnie inaczej (pipe
, fork
, dup2
, …), ale ogólna idea jest podobna. Jak widać, kilkoma małymi rurkami można zdziałać całkiem sporo :]
POSIX (Portable Operating System Interface) to taki śmieszny “standard dla systemów operacyjnych”, opracowany przez znane skądinąd konsorcjum IEEE. Celem jego stworzenia było zapewnienie jak największej zgodności w działaniu (lub niedziałaniu, rzecz jasna) dla aplikacji pracujących pod kontrolą różnych wariantów Uniksa. W tym celu określone jest pokaźnych rozmiarów API, które zajmować ma się takimi rzeczami jak procesy, wątki, sygnały, I/O, gniazda sieciowe, i tak dalej.
To, co POSIX w tym zakresie teoretycznie oferuje, jest w gruncie rzeczy całkiem zadowalające. Standard nie zabrania zresztą, by implementujące go systemy operacyjne dodawały do tego jakąś własną funkcjonalność.
Dlaczego więc zgodność poszczególnych systemów z POSIX-em jest w przybliżeniu odwrotnie proporcjonalna do ich popularności? :-) Wbrew pozorom te co bardziej znane, jak różnego rodzaju BSD i dystrybucje Linuksa, nie są pod tym względem doskonałe. Jedynie znacznie bardziej specyficzne Solarisy, QNX-y oraz, co ciekawe, Mac OS X spełniają standard POSIX-a w pełni.
A co z naszymi ulubionymi okienkami? W ich przypadku jesteśmy oczywiście bardzo, bardzo daleko… ale tylko do czasu. Windows można bowiem dość prosto doprowadzić do pełnej zgodności przy pomocy takich pakietów jak Microsoft Windows Services for UNIX czy Cygwin. Może to być dobra pomoc dla tych, którzy chcą pisać przenośne aplikacje bez opuszczania przyjaznego środowiska okienek.
Unicode to ciekawy wynalazek. Zamiast stosować wymyślne sposoby na “przełączanie” sposobu interpretowania zwykłych 8-bitowych znaków, ktoś mądry wymyślił po prostu, że obecnie nie ma większych przeciwwskazań, aby zwykły tekst zajmował dwa razy więcej miejsca niż dotychczas. Powstał więc standard UTF-16, w którym stron kodowych nie ma, a każdy znak jest zapisany za pomocą jednego z 65536 kodów.
Ale jak to zwykle bywa z rozwiązaniami, które mają rozwiązywać istniejące problemy, Unicode natychmiast stworzył swoje własne :) Oprócz tego, że założone 16 bitów szybko okazało się za małe (stąd istnienie także UTF-32), powstał też szereg kłopotów praktycznych. Jednym z nich jest choćby to, że żyjemy w okresie przejściowym (który zresztą trwa już wybitnie długo) i że w użyciu jest zarówno unikod, jak i zwykłe ANSI. A na pierwszy rzut oka (ludzkiego i programowego) tekst jest po prostu ciągiem bajtów i bez zewnętrznych wskazówek nie jest możliwe określenie w stu procentach, czy został on zapisany w ANSI, UTF-8, UTF-16 czy UTF-32. Co więcej, znaki Unicode są oczywiście liczbami, a ponieważ zasadniczo zajmują one więcej niż jeden bajt, pojawia się problem z ustaleniem właściwej kolejności tych bajtów (little-endian lub big-endian).
Oba te problemy ma rozwiązywać tzw. znacznik porządku bajtów (Byte Order Mark), ale oczywiście jak większość dobrych praktyk, także i jego umieszczanie na początku dokumentów nie jest zbyt popularne :)
Z programistycznego punktu widzenia sprawa nie wygląda aczkolwiek aż tak źle i w większości przypadków jesteśmy w jednej z dwóch sytuacji. Pierwsza z nich to “nieświadome” korzystanie z unikodu (czy może raczej “szerokich”, dwubajtowych znaków), bo został o niego oparty używany przez nas język oraz platforma; najlepszymi przykładami są tu .NET i C# oraz Java.
Druga sytuacja to możliwość wyboru, z jakiego systemu będziemy korzystali. Bardzo dobre jest to, że prawie zawsze możemy zdecydować się na… oba, czyli potencjalne kompilowanie dwóch wersji: ANSI i Unicode. Pod Windows na przykład w programach pisanych w C++ wystarczy przestrzegać kilku prostych i dobrze znanych zasad:
char
(lub wchar_t
) należy – wszędzie tam, gdzie chodzi nam o znaki – używać typu TCHAR
, który w zależności od wersji zostanie zamieniony na jeden z tych dwóch.TEXT
(czyli TEXT("Coś")
zamiast po prostu "Coś"
), dzięki czemu zostaną one skompilowane jako łańcuchy ANSI lub Unicode.Niestety, słowo ‘niektóre’ sytuuje się dość daleko od ‘wszystkie’ i dlatego ostatecznie nie jest tak różowo. Czołowe miejsce na liście “niewspółpracujących” zajmuje standardowa biblioteka C++. Z nią trzeba sobie poradzić we własnym zakresie.
Nie jest to aczkolwiek bardzo trudne, jako że każdy kompilator definiuje makro dla rozróżnienia wersji ANSI i Unicode. Visual C++ na przykład włącza w tym przypadku symbol _UNICODE
, którego możemy użyć do stworzenia odpowiednich aliasów:
Możemy je umieścić we własnym pliku nagłówkowym i dołączać we własnych projektach. Ponieważ jednak typów zależnych od znaków jest w STL całkiem sporo, można z powodzeniem użyć chociażby rozwiązania zamieszczonego na CodeProject, w razie potrzeby rozszerzając je o kolejne aliasy.
Ponieważ nowoczesne systemy operacyjne – takie jak Windows – starają się być przyjazne dla wszystkich użytkowników, już od dawna wyposażone są w specjalne funkcje ułatwiające korzystanie z komputera osobom z różnymi rodzajami niepełnosprawności. Jest więc możliwość ustawienia większego kontrastu interfejsu użytkownika, włączenia śladu po kursorze, łatwiejszego wciskania skrótów klawiaturowych, i tak dalej.
Chociaż większość tych możliwości zdaje się być przeznaczona wyłącznie dla wspomnianych użytkowników, to jednak przynajmniej dwie mogą być użyteczne dla wszystkich korzystających z Windows. Osobiście znam i często korzystam z dwóch takich, a mianowicie:
Jak wszystkie ułatwienia dostępu, możemy je włączyć przy pomocy odpowiedniego apletu w Panelu sterowania: Centrum ułatwień dostępu w Windows Vista lub Ułatwienia dostępu w starszych wersjach systemu.
Część paska zadań w systemie Windows obok zegara pokazującego aktualny czas to tak zwany zasobnik systemowy (system tray) lub obszar powiadomień. Tradycyjnie widnieją w nim ikonki tych aplikacji, które zasadniczo działają w tle i przez większość czasu nie wymagają interwencji użytkownika. W przeciwieństwie jednak do procesów usług, których zupełnie nie widać (o ile się nimi specjalnie nie zainteresujemy), programy te czasami wymagają naszej uwagi. I na różne sposoby mogą się starać ją zwrócić.
Możliwa jest chociażby zmiana wyglądu ikonki, polegająca na przykład na jej miganiu. Istnieje jednak wówczas ryzyko, że użytkownik tego po prostu nie zauważy – zwłaszcza, że począwszy od Windows XP ikony dłuższy czas nieaktywne są automatycznie przez system ukrywane. Poza tym zaprogramowanie migania ikony jest wbrew pozorom nieco kłopotliwe, bo całą tę animację trzeba zakodować ręcznie. A ponadto nie daje to żadnych możliwości przekazania, o co tak naprawdę naszemu programowi chodzi.
Na drugim końcu skali mieści się z kolei wyświetlenie standardowego okna komunikatu (message box). Wymaga to tylko wywołania jednej funkcji i daje gwarancję, że użytkownik nie przeoczy tej informacji. Aczkolwiek prawie pewnym skutkiem jest też podniesienie poziomu irytacji tegoż użytkownika, któremu nagle przerywamy pracę, by zasygnalizować jakąś błahostkę. Lepiej więc nie nadużywać tej metody i stosować ją do sytuacji naprawdę ważnych.
W zwykłych przypadkach bardzo dobrze natomiast sprawdza się wynalazek wprowadzony w Windows 2000, czyli tzw. dymki balonowe (balloon tips). Faktycznie wyglądają one podobnie do dymków w komiksach i pojawiają się tuż nad paskiem zadań, wskazując na ikonę, której dotyczą. Mogą przekazywać sam tekst, ale można też opatrzyć je tytułem i jedną z czterech systemowych ikon – zupełnie jak zwykłe okna komunikatu. W przeciwieństwie jednak do nich, dymki nie wyskakują znienacka i nie wymagają żadnego potwierdzania, gdyż po pewnym (ustalonym) czasie same znikają.
Niestety, niewiele aplikacji używa dymków balonowych – a szkoda, bo to jeden z ciekawszych elementów interfejsu użytkownika wprowadzonych w nowszych wersjach Windows. Prawdę mówiąc sądzę, że nawet w przypadku programów, które domyślnie “nie siedzą” w zasobniku, wykorzystanie dymków jest lepsze w przypadku zdarzeń zachodzących w tle niż np. miganie paskiem tytułowym okna. To mniej stresu i więcej informacji dla użytkownika :)
Pozostaje jeszcze kwestia programistyczna, czyli jak taki dymek wyświetlić. W Windows Forms jest to proste i wymaga na przykład wywołania metody ShowBalloonTip
komponentu NotifyIcon
. W klasycznym Windows API jest nieco gorzej, gdyż robimy to tę samą funkcją Shell_NotifyIcon
, którą wykorzystuje się do każdej innej operacji na ikonce w trayu. Dla wygody najlepiej więc opakować ją we własną klasę.