Programy działające w tle (np. komunikatory) często używają różnego rodzaju “wyskakujących” powiadomień, które w założeniu mają pojawiać się tuż obok zasobnika systemowego (system tray), gdzie wspomniane programy mają swoje ikonki. W rzeczywistości pozycja tych komunikatów jest często hardcode‘owana w okolicach prawego dolnego rogu ekranu. Jeśli więc użytkownik przypadkiem ma pasek zadań gdzie indziej, to wtedy cały misterny plan idzie w… las ;]
Dlatego lepszym rozwiązaniem jest sprawdzanie, gdzie tak naprawdę ów pasek się znajduje. Przez długi czas można było to zrobić poprzez zwyczajne pobranie jego uchwytu (HWND
) i odczytanie wymiarów tak, jak każdego innego okna – np. przez GetWindowRect
:
Począwszy od Windows Vista nie jest to jednak możliwe, gdyż ze względu na bezpieczeństwo (tzw. izolacja sesji 0) nie da się już tak po prostu pobrać uchwytu do paska zadań ze zwykłej aplikacji. Istnieje na szczęście przenośny sposób na pobranie jego pozycji; w tym celu trzeba się posłużyć funkcją powłoki systemowej SHAppBarMessage
(z nagłówka shellapi.h i biblioteki shellapi.lib):
Tak możemy otrzymać nie tylko wymiary prostokąta okalającego pasek, ale też informację o tym, przy której krawędzi się on znajduje. Jest ona zawarta w polu uEdge
struktury zwracanej przez funkcję SHAppBarMessage
i równa którejś ze stałych ABE_BOTTOM
, ABE_TOP
, itd.
Mając te dwie informacje możemy nie tylko wyświetlać nasze powiadomienia w odpowiednim miejscu, ale też zapewnić, żeby w razie ich nagromadzenia “rosły” one w odpowiednim kierunku.
Jedną z rzeczy, którą musiałem wykonać w trakcie (tymczasowej) przesiadki na komputer stacjonarny, było dogłębne sprawdzenie go programem antywirusowym – co “z pewnych względów” nie było czynione przez dobrych kilka miesięcy :) Skan nie wykrył aczkolwiek w zasadzie nic bardziej niebezpiecznego niż typowe ‘śledzące ciasteczka’ (tracking cookie). Dopiero później zaczęło się robić ciekawie.
Zło (i to przez duże Z) zostało bowiem wkrótce wykryte przez skaner działający w tle, który zidentyfikował je heurystycznie jako rootkit i oprócz tej informacji nie powiedział zresztą nic więcej. Przyznam, że było to moje pierwsze zetknięcie z tego typu szkodnikami, więc tym bardziej energicznie zabrałem się do rozwiązywania problemu – zwłaszcza że ze wszystkich typów złośliwego oprogramowania to właśnie rootkity sa zdecydowanie najgroźniejsze.
Na szczęście ten okazał się dość znanym i nawet nieszczególnie niebezpiecznym drobnoustrojem, przenoszącym się – o dziwo – nie przez sieć, lecz przez dyski wymienne. Zapewne dlatego też moje kilkugodzinne starcie z przeciwnikiem zakończyło się (prawdopodobnie) sukcesem, zaś wnioski z tego są następujące:
Krótko mówiąc, wykrywanie i walka z tym rodzajem szkodliwego oprogramowania to coś znacznie poważniejszego niż wciśnięcie przycisku Scan i czekanie na wyniki :] Rezultaty też nigdy nie są pewne.
O reinstalacji systemu na komputerze stacjonarnym trzeba więc będzie pomyśleć jak najszybciej, a od wczoraj stało się to prostsze, gdyż mój laptop wrócił z naprawy. Okazało się, że konieczna była wymiana płyty głównej (!) – oryginalna prawdopodobnie miała fabryczną usterkę… No cóż, dobrą stroną jest to, że za tę skomplikowaną operację nie musiałem zapłacić ani grosza; niech żyją gwarancje :)
Wiele różnych rozbudowanych programów – jak np. IDE lub pakiety biurowe – zawiera mechanizmy rozszerzające funkcjonalność windowsowego Schowka. Oznacza to najczęściej możliwość zachowania więcej niż jednego wycinka naraz i przełączanie się między nimi, gdy chcemy wkleić gdzieś któryś z nich.
Dotąd nie byłem zbyt entuzjastycznie nastawiony do takiej funkcjonalności – może dlatego, że posiadanie więcej niż jednego Schowka było poza zasięgiem moich możliwości pojmowania ;) Zdarzyło się jednak, że kilka razy potrzebowałem wrócić do tekstu, który już “nieopatrznie” nadpisałem kilkoma kolejnymi skopiowaniami do Schowka. Wtedy też zacząłem rozglądać się za aplikacją, która w jakiś sensowny sposób rozszerza możliwości systemowego Schowka, dodając do niego więcej ‘slotów’.
Poszukiwania nie były niestety specjalnie owocne. Najlepsze, co udało mi się znaleźć, to program o wielce oryginalnej nazwie MultiClipBoard. Narzędzie to chowa się do systemowego zasobnika i pozwala posiadać nieograniczoną liczbę niezależnych Schowków na wycinaną treść. Potrafi też tworzyć nowy przy każdym kopiowaniu, dzięki czemu niczego nie zgubimy. Niestety, nie oferuje zbyt wielu przydatnych skrótów klawiszowych, umożliwiających chociażby cofnięcie się do poprzednio wyciętego elementu.
Ale oczywiście lepsze to niż nic – przynajmniej na razie. Bo kto wie, może w Windows 7 zamiast kolejnych wodotrysków zobaczymy coś użytecznego, jak właśnie nieco bardziej inteligentną obsługę Schowka. Chociaż to pewnie tylko pobożne życzenia :)
Chociaż Windows API ciągnie za sobą skutki kilkunastu lat wstecznej kompatybilności, nie znaczy to, że nie pojawiają się tam nowe możliwości. Ponieważ jednak wszystkie funkcje są importowane z DLL-i w sposób statyczny, nie jest możliwe, by każda nowsza była dostępna automatycznie. W przeciwnym wypadku program, który faktycznie działałby bez problemu np. na Windows 98 wymagałby w praktyce najnowszej wersji systemu – mimo tego, że nie korzysta z żadnej nowej funkcjonalności.
Niekiedy jednak nie chcemy ograniczać się do “wspólnego mianownika” wszystkich aktualnie obsługiwanych wersji Windows i chcemy użyć czegoś, co rzeczywiście jest dostępne począwszy od jakiejś nowszej edycji systemu. Co wtedy zrobić?
W dokumentacji (czyli MSDN) jest na szczęście dokładnie zaznaczone to, w której wersji Windows wprowadzoną daną funkcję czy strukturę. Ale żeby odpowiedni symbol został włączony do kompilacji i linkowania, należy jeszcze przed dołączeniem windowsowego nagłówka (zazwyczaj windows.h) zdefiniować odpowiednie makro.
A właściwie to nawet więcej niż jedno, jako że MSDN wspomina o czterech. Są to:
WINVER
i _WIN32_WINNT
, wskazujące na liczbową wersję systemu. Wartość 0x0502
oznacza na przykład wersję 5.1, czyli Windows XP z SP2._WIN32_IE
, mające zawierać docelową wymaganą wersję Internet ExploreraNTDDI_VERSION
, ustawialne na którąś z predefiniowanych stałych przypisanych każdej większej edycji Windows (np. NTDDI_WINXPSP1
lub NTDDI_WIN2K
)Pierwsze trzy są już uważane za przestarzałe i aktualnie używać należy NTDDI_VERSION
, które jest zresztą znacznie wygodniejsze. W praktyce dobrze jest jednak ustawiać wszystkie makra, zwłaszcza jeśli nasz kod może być potencjalnie kompilowany na wielu różnych wersjach Platform SDK. I tak na przykład jeśli chcemy wykorzystać pule wątków (thread pools) dostępne począwszy od Windows Vista, powinniśmy dodać taki oto kod:
Po wartości makr odpowiadające wszystkim edycjom Windows należy udać do MSDN, do sekcji:
Win32 and COM Development > Development Guides > Windows API > Using Windows Headers
W Windows obsługa zdarzeń opiera się na koncepcji pętli komunikatów (message loop). Taka pętla powinna nieustannie kręcić się w każdym wątku, w którym tworzone są okna, i na bieżąco pobierać pojawiające się komunikaty o zdarzeniach. Dzięki temu użytkownik może przeprowadzać interakcje z interfejsem programu. Dlatego też wszelkie dłuższe działania (np. złożone obliczenia lub odczyt/zapis, zwłaszcza przez sieć) mogą sprawić, że aplikacja wyda się zawieszona. Pętla komunikatów nie będzie bowiem pobierać i przetwarzać gromadzących się w kolejce zdarzeń.
Typowym rozwiązaniem jest umieszczenie takich czasochłonnych operacji w osobnym wątku. Czasem jest to oczywiście niezbędne, lecz wielowątkowość, jak wiadomo, oprócz korzyści wprowadza dodatkowe problemy, które należy uwzględnić – jak choćby synchronizacja dostępu do danych.
Jeśli jednak nasza długa operacja daje się podzielić na kilka mniejszych, to możemy wówczas zastosować inne rozwiązanie. Można mianowicie co jakiś (krótki) czas pobierać oczekujące w kolejce komunikaty i zajmować się nimi, dzięki czemu interfejs będzie miał szansę zareagować na akcje użytkownika. Zazwyczaj oznacza to odpowiednio częste wywoływanie funkcji w rodzaju System.Windows.Forms.Application.DoEvents
z .NET lub TApplication.ProcessMessages
z VCL (Delphi). Jeśli na przykład dokonujemy jakichś obliczeń w pętli, wspomniane funkcje można wywoływać w każdym jej cyklu.
Jest nieco gorzej, jeśli piszemy z użyciem samego Windows API. Podobną funkcję musimy wtedy napisać sami. Nie jest to na szczęście trudne – należy po prostu “na chwilę” uruchomić naszą pętlę komunikatów, aby przetworzyła oczekujące zdarzenia:
Korzystamy z PeekMessage
, która sprawdza, czy w kolejce nie ma już więcej zdarzeń i informuje nas o tym. Trzeba tylko pamiętać, że wśród tych zdarzeń może pojawić się WM_QUIT
, który to komunikat oznacza konieczność zamknięcia aplikacji. Sposobem na jego obsłużenie może być chociażby wartość zwrócona z naszej funkcji; wywołujący będzie wówczas wiedział, że powinien zakończyć to, co aktualnie robi, gdyż program ma zostać zamknięty.
Oczywiście postać powyższej pętli powinna tak naprawdę być identyczna jak tej głównej, w funkcji WinMain
– a więc niekoniecznie taka, jak powyżej. To się aczkolwiek zdarza wtedy, gdy korzystamy z okien dialogowych, akceleratorów menu i tym podobnych rzeczy. Samo to wymaga jednak na tyle dużej biegłości w Windows API, że odpowiednie przerobienie zaprezentowanej funkcji to przy tym naprawdę mały pikuś ;-)
Czasami zdarza się, że chcemy usunąć plik, który okazuje się być używany przez inny proces lub system operacyjny. Często nie mamy pojęcia, dlaczego tak jest i kto ma jakiś interes w trzymaniu blokady na tym właśnie pliku. Zwłaszcza Vista, będąca paranoicznie przewrażliwiona na punkcie zachowania integralności systemu plików, jest bardzo restrykcyjna pod względem swobody kasowania zbiorów będących w użyciu.
Oczywiście w takiej sytuacji można zawsze zrestartować system, ale trudno uznać to za wygodne rozwiązanie :) Dobrze byłoby więc mieć sposób na dowiedzenie się, jaki proces używa danego pliku, aby ewentualnie go zakończyć lub w skrajnym przypadku po prostu ubić.
I tu jest problem, bowiem w Windows nie ma na to prostego sposobu. Dotyczy to między innymi istnienia jakiegoś API, które by na to pozwalało; w najlepszym przypadku należy posłużyć się narzędziami z DDK, czyli pakietu służącego do… pisania sterowników działających w trybie jądra (kernel mode). Podobnie rzecz ma się z istnieniem wbudowanych w system programów, służących do tego celu (a więc czegoś podobnego do uniksowej komendy lsof). Odpowiednie narzędzia są jedynie częścią pakietów dodatkowych, jak Windows Server 2003 Resource Kit (niekompatybilny z Vistą) czy Windows Sysinternals. Mogą być one jednak dość kłopotliwe w obsłudze.
Dlatego podzielę się moim niedawnym odkryciem, którym jest mały i niezwykle przydatny programik o nazwie Unlocker. Potrafi on dokładnie to, o czym jest tu mowa: wylistować procesy, które używają danego pliku. Działa on przy tym na każdej sensownej wersji Windows, integruje się z menu kontekstowym plików dla większej wygody, no i jest do tego aplikacją freeware.
Rzadko robię czemuś aż taką “reklamę”, ale w tym przypadku uznałem, że tak dobrym i użytecznym narzędziem należy się niezwłocznie podzielić ze światem :)
System Windows od niepamiętnych czasów (a dokładniej od wersji 95) pozwala na tworzenie skrótów (shortcut), czyli plików będących odwołaniami do innych plików lub katalogów. Skróty mają zwykle rozszerzenie .lnk i występują głównie w katalogach tworzących strukturę Menu Start.
Wewnętrznie nie ma w nich nic pomysłowego, gdyż są to zwykłe pliki w formacie na wpółbinarnym, zawierające docelową ścieżkę lub URI (np. adres internetowy). Ich obsługa realizowana jest na poziomie Eksploratora Windows, więc nie są one specjalnie funkcjonalne. Otwarcie pliku skrótu w dowolnym programie nie spowoduje na przykład odczytania zawartości docelowego pliku, lecz samego skrótu (który zwykle ma kilkaset bajtów). Podobnie skrót do katalogu Foo o nazwie Bar.lnk nie spowoduje, że do pliku Foo/Coś.txt odwołamy się poprzez Bar.lnk/Coś.txt.
Taką funkcjonalność zapewniają jednak linki twarde i symboliczne, obecne w nowszym wersjach NTFS. Są one bardzo podobne do swoich odpowiedników w systemach plików ext2 i ext3 (uniksowych). I tak:
fsutil hardlink create link cel
, a w Windows Vista dodatkowo mklink /H link cel
. Programowo robi się funkcją WinAPI o nazwie CreateHardLink
.mklink
(trzeba jednak zaznaczać jawnie, czy robimy link do pliku czy do katalogu). We wcześniejszych wersjach możliwe jest jedynie tworzenie NTFS junctions, będących “linkami” tylko do katalogów oraz obarczonych innymi ograniczeniami (na przykład Eksplorator w Windows XP dość słabo sobie z nimi radzi).Jak nietrudno zauważyć, podstawową zaletą linków nad zwykłymi skrótami jest to, że te pierwsze są obsługiwane na poziomie systemu plików. Wobec tego działają one w dowolnym programie i zupełnie przezroczyście dla aplikacji. Trochę szkoda, że ten przydatny drobiazg pochodzący z systemów uniksowych dopiero teraz pojawia się w Windows… ale przecież lepiej późno niż wcale ;]