Wprowadzenie do programowania w WIN32 API Podstawy architektury Windows Witold Bartkiewicz
Procesy i aplikacje Termin "proces" i bardziej popularny termin "aplikacja" używane są czasami wymiennie. Tym niemniej, w środowisku Windows istnieje jednak różnica między tymi dwoma pojęciami. Aplikacja jest statyczną sekwencją instrukcji, tworzącą plik wykonalny. Proces natomiast jest instancją uruchomionej aplikacji.
Procesy Programu wykonalnego Prywatnej przestrzeni adresowej w pamięci Zasobów systemowych, takich jak pliki, potoki, porty komunikacyjne i semafory Co najmniej jednego wątku, gdzie przez wątek rozumiemy ścieżkę wykonania Proces składa się z:
Pamięć w systemie Windows Pamięć fizyczna Złożona jest z pewnej ilości fizycznej RAM Pamięć wirtualna Złożona z 4 GB adresów, lub 2 32 bajtów adresowalnej pamięci, dostępnej dla aplikacji. Każda aplikacja może alokować 2GB adresów, drugie 2GB rezerwuje na swoje potrzeby system operacyjny W systemie operacyjnym Windows mamy do czynienia z dwoma rodzajami pamięci:
Pamięć fizyczna i wirtualna Aplikacja nigdy nie ma bezpośredniego dostępu do pamięci fizycznej. Menedżer pamięci wirtualnej (mechanizm systemowy) kontroluje wszystkie odwołania do pamięci fizycznej za pośrednictwem żądań dostępu z użyciem adresów wirtualnych.
Jak działa system pamięci wirtualnej System operacyjny tworzy nowy proces. Każdemu procesowi przydzielanych jest na jego potrzeby 2GB adresów wirtualnych (nie pamięci). Aktualnie niezbędny kod aplikacji jest ładowany do pamięci fizycznej, ponadto menedżer pamięci wirtualnej mapuje go w określone miejsce w wirtualnej przestrzeni adresowej aplikacji. Adres wirtualny nie ma związku z lokalizacją kodu w pamięci fizycznej.
Jak działa system pamięci wirtualnej Jeśli aplikacja korzysta z bibliotek dynamicznych, DLL mapowane są w przestrzeń adresową procesu i w miarę potrzeb ładowane do pamięci fizycznej. Miejsce dla obiektów takich jak dane, stosy alokowane jest w pamięci fizycznej i mapowane w przestrzeń adresową. Aplikacja rozpoczyna wykonanie, korzystając z adresów w wirtualnej przestrzeni adresowej, a menedżer pamięci wirtualnej mapuje każde odwołanie do jego fizycznej lokalizacji.
Organizacja pamięci wirtualnej
Struktura adresu wirtualnego
Adres fizyczny strony
Przyczyny stosowania pamięci wirtualnej Pozwala ona na uniknięcie defragmentacji pamięci w środowisku wieloprocesowym, bez konieczności fizycznego przesuwania informacji. Ukrywa on rozdrobnioną, strukturę stronicowanej pamięci fizycznej, pozwalając aplikacji widzieć ją jako płaski i ciągły blok kolejnych adresów. Umożliwia zwiększenie ilości dostępnej pamięci. Izoluje aplikację od mechanizmów wymiany stron między pamięcią operacyjną i dyskiem.
Przyczyny stosowania pamięci wirtualnej Wprowadza (w NT) ścisłe rozgraniczenie między pamięcią różnych procesów. Każdy proces ma swój własny katalog stron. Nie ma więc możliwości uszkodzenia pamięci innego procesu np. na skutek przypadkowego i błędnego zapisu. Ułatwia aplikacjom zarządzanie pamięcią. Ukrywa złożony mechanizm zarządzania pamięcią fizyczną, pozwalając widzieć ją jaki płaski obszar 2GB adresów.
Wątki Każdy proces rozpoczyna się pojedynczym działającym wątkiem (ścieżka wykonania). Może jednak uruchamiać większą ich liczbę. Wszystkie wątki procesu działają w jego przestrzeni adresowej i korzystają z zasobów przydzielonych temu procesowi. Wątki w systemie Windows wykonywane są współbieżnie, przy czym jest tzw. współbieżność z wywłaszczaniem.
Współbieżność System operacyjny przydziela każdemu wątkowi stosunkowo krótki (dziesiąte milisekund) przedział czasu procesora. Wątek wykonuje się do chwili wyczerpania przedziału czasu, lub do chwili gdy musi poczekać na jakieś zasoby. Następnie wątek jest wywłaszczany, tzn. zapisywany jest jego kontekst, system operacyjny ładuje kontekst drugiego wątku i uruchamia go w kolejnym przedziale czasu procesora.
Podstawowe biblioteki WIN32 API Kernel (KRNL386.EXE – 16 bitowy, KERNEL32.DLL - 32 bitowy) – obsługuje wszystkie funkcje jądra systemu operacyjnego, takie jak zarządzanie pamięcią, plikowe I/O, uruchomianie zadań, itp. User (USER.EXE, USER32.DLL) – obsługuje interfejs użytkownika i całą logikę okien. GDI (GDI.EXE, GDI32.DLL) – jest interfejsem urządzeń graficznych, pozwalającym programowi wyświetlać tekst i grafikę na ekranie lub wydrukować je na drukarce.
Odwołania do funkcji WIN32 API Podsystemy Windows, ujęte są kilka tysięcy funkcji, znajdujących się w bibliotekach dynamicznych. W programach korzystamy z funkcji systemu praktycznie w taki sam sposób jak ze zwykłych funkcji bibliotecznych RTL, takich jak np. printf, strlen.
Biblioteki dynamiczne i statyczne Różnica jest taka, że kod maszynowy zwykłej (statycznie linkowanej) funkcji bibliotecznej włączany jest do programu wynikowego podczas kompilacji, podczas gdy kod funkcji systemu ładowany jest z bibliotek dynamicznych podczas wykonania programu.
Ładowanie DLL Plik EXE w Windows zawiera jedynie odnośniki do funkcji ładowanych z bibliotek dynamicznych. Gdy program ładowany jest do pamięci, w miarę potrzeby ładowane i mapowane w jego przestrzeń adresową są również biblioteki dynamiczne, zaś odwołania w programie odwzorowywane są na punkty wejścia wykorzystywanych funkcji.
Pliki EXE i DLL Program korzystający z bibliotek dynamicznych dla swojego wykonania wymaga więc dostarczenia obok pliku EXE również plików DLL zawierających wykorzystywane biblioteki. W przypadku WIN32 API odpowiednie biblioteki DLL dostarczane są wraz z systemem operacyjnym.
Linkowanie programu z DLL Podczas linkowania programu dla każdej biblioteki dynamicznej DLL niezbędne jest również posiadanie specjalnej biblioteki importowej (zwykłej, statycznej typu LIB). Zawiera ona nazwy bibliotek dynamicznych i informacje o zawartych w nich funkcjach. Linker używa tej informacji do zbudowania tabeli w pliku EXE, którą Windows wykorzystuje do odwzorowania wywołań na funkcje podczas ładowania programu.
Kompilacja z WIN32 API W przypadku Win32 API biblioteki importowe oraz pliki nagłówkowe niezbędne do kompilacji funkcji bibliotecznych, dostarczane są razem ze środowiskiem programistycznym.
Dlaczego korzystamy z WIN API Poznanie Win API pozwala na zrozumienie mechanizmów działania Windows oraz specyfiki budowy aplikacji w tym środowisku Programy w API wymagają jedynie systemowych bibliotek dynamicznych, które znajdują się na każdym komputerze korzystającym z Windows. Nie wymagają więc dystrybuowania dodatkowych DLL
Dlaczego korzystamy z WIN API U podstaw każdego innego środowiska leży Win API. MFC, VCL JFC, Visual Basic, czy inne środowiska są jedynie opakowaniami wywołującymi funkcje Win API. Ułatwiają one korzystanie z niego, dodają nowe funkcje, ale u ich podstaw leżą odwołania do funkcji systemowych Dla dogłębnego zrozumienia działania innych środowisk niezbędne jest więc zrozumienie działania Win API
Program HelloWorld w środowisku znakowym #include int main () { printf ("Hello, world\n"); return 0 ; }
Program HelloWorld w Windows #include int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { MessageBox(NULL,TEXT("Hello, world!"),TEXT("HelloMsg"),0); return 0 ; }
Podstawowy plik nagłówkowy WINDOWS.H jest głównym plikiem nagłówkowym, który włącza inne pliki nagłówkowe Windows. Najważniejsze z nich, to: WINDEF.H- podstawowe definicje typów. WINNT.H- definicje typów dla unikodu. WINBASE.H- funkcje jądra systemu WINUSER.H- funkcje interfejsu użytkownika WINGDI.H- funkcje interfejsu urządzeń graficznych
Punkt startowy programu int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance HINSTANCE hPrevInstance, // handle to previous instance LPSTR lpCmdLine, // pointer to command line int nCmdShow // window show state );
HINSTANCE hInstance Pierwszy parametr funkcji WinMain, tzw. uchwyt realizacji. Jest to jednoznaczny identyfikator aplikacji w systemie, przekazywany przez Windows podczas jej uruchomienia. Wymagany jest on jako argument przy wywołaniach wielu innych funkcji Windows
HINSTANCE hPrevInstance Uchwyt do poprzedniej realizacji programu. We wcześniejszych wersjach Windows każde uruchomieniem programu nie tworzyło odrębnego procesu, lecz kolejną jego realizację. Wszystkie te realizacje korzystały z tego samego kodu i pamięci tylko do odczytu (zwykle takich zasobów jak menu, wzorce okien dialogowych, itp.). W 32 bitowej wersji Windows zrezygnowano z tej koncepcji. Drugi parametr funkcji WinMain ma zawsze wartość NULL
PSTR szCmdLine Trzecim parametrem jest wiersz poleceń użyty do uruchomienia programu.
int iCmdShow Ostatni parametr określa postać okna w jakim uruchomiony zostanie program – standardowym, zminimalizowanym lub zmaksymalizowanym. Zastosowanie tego parametru omówione zostanie dokładniej na kolejnych zajęciach.
Funkcja MessageBox int MessageBox( HWND hWnd, // handle to owner window LPCTSTR lpText, // text in message box LPCTSTR lpCaption, // message box title UINT uType // message box style );
Liczba i rodzaj przycisków #define MB_OK 0x L #define MB_OKCANCEL 0x L #define MB_ABORTRETRYIGNORE 0x L #define MB_YESNOCANCEL 0x L #define MB_YESNO 0x L #define MB_RETRYCANCEL 0x L
Wybór przycisku domyślnego #define MB_DEFBUTTON1 0x L #define MB_DEFBUTTON2 0x L #define MB_DEFBUTTON3 0x L #define MB_DEFBUTTON4 0x L
Rodzaj wyświetlanej ikony #define MB_ICONHAND 0x L #define MB_ICONQUESTION 0x L #define MB_ICONEXCLAMATION 0x L #define MB_ICONASTERISK 0x L #define MB_ICONWARNING MB_ICONEXCLAMATION #define MB_ICONERROR MB_ICONHAND #define MB_ICONINFORMATION MB_ICONASTERISK #define MB_ICONSTOP MB_ICONHAND
Przykład 2 #include int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevinstance, PSTR szCmdLine, int iCmdShow) { while ( MessageBox( NULL, TEXT("Hello world! Czy zakończyć?"), TEXT("HelloMsg"), MB_YESNO|MB_DEFBUTTON2|MB_ICONQUESTION ) == IDNO ) ; MessageBox(NULL,TEXT("Koniec"),TEXT("HelloMsg"),0); return 0; }
Wartości zwracane przez funkcję MessageBox IDABORTWybrano przycisk Abort. IDCANCELWybrano przycisk Anuluj. IDCONTINUEWybrano przycisk Kontynuuj. IDIGNOREWybrano przycisk Zignoruj. IDNO Wybrano przycisk Nie. IDOK Wybrano przycisk OK. IDRETRY Wybrano przycisk Powtórz. IDTRYAGAIN Wybrano przycisk Spróbuj ponownie. IDYES Wybrano przycisk Tak.