arrowHome Tuesday, 07 September 2010  



 
Google
Web winapi.org
Main Menu
Home
News
FAQ
Links
Download
Kontakt
FORUM

Artykuły
Podstawy
GDI i Multimedia
Kontrolki
inne
Winapi + asm
WinSock
Soft
Login Form
Login

Hasło

Zapamiętaj mnie
Nie pamiętasz hasła?
Nie masz konta? Załóż je sobie
Zasada działania programu w WinAPI Drukuj E-mail
Oceny: / 128
KiepskiBardzo dobry 
Napisał pinolec   
Thursday, 19 February 2004

Nie mam wyboru. Pokazałem jak wygląda typowa aplikacja pod Windows API. Teraz muszę pokazać co ona robi. Jeżeli pierwszy raz widzisz taki program to pewnie nie wiesz co jest co. Ja gdy pierwszy raz coś takiego ujrzałem to dostałem białej gorączki ;-) Minie trochę czasu zanim wszystko zrozumiesz. Więc nie staraj się zrozumieć i zapamiętać wszystkiego na raz. Polecam raczej metodę wprowadzania własnych zmian w programie. Na początku to właśnie sprawia najwięcej przyjemności. Ale najpierw postaraj się przeczytać uważnie ten tekst i zrozumieć ile się da. Jeśli czegoś nie zrozumiesz nie przejmuj się. Nie od razu Rzym zbudowano. Program zawiera dwie niezbędne mu do "życia" funkcje: WinMain i MainWndProc. Co one robią? W uproszczeniu WinMain tworzy nam okno i przekazuje komunikaty właśnie do funkcji MainWndProc. Ale zacznijmy od początku. Ponieważ główną (main) funkcją jest funkcja WinMain to zaczniemy od niej.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)

Pierwszym parametrem funkcji WinMain jest tzw. uchwyt realizacji. Pewnie spytacie co to takiego ten uchwyt. Jest to po prostu liczba, którą w tym przypadku Windows przydziela temu programowi, aby mógł go odróżniać od innych ;-) Drugim argumentem jest uchwyt realizacji poprzedniej kopii tego programu. Jest to pozostałość po Win16. Obecnie nie ma znaczenia. Trzecim argumentem jest wiersz poleceń. Uruchamiając program możemy podać jakieś parametry np. pliku który ma być uruchomiony z tego programu. Czwarty z kolei parametr wskazuje jak ma być uruchamiany nasz program - zminimalizowany, na pełnym ekranie itd.

A co w niej mamy:

MSG msg;
WNDCLASS wndclass;
HWND hWnd;

Są to nic innego jak zmienne, których w API nie brakuje ;-)

MSG - struktura komunikatu
WNDCLASS - struktura klasy okna
HWND - uchwyt okna

Następnie wypełniamy wcześniej zdefiniowaną strukturę wndclass.

wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = MainWndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)COLOR_WINDOW;
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = app;

Spokojnie nie jest to takie straszne. Na początku trzeba wiedzieć, że każde okno musi posiadać klasę. Najważniejsza jest nazwa klasy (wndclass.lpszClassName) oraz nazwa funkcji, która będzie obsługiwać komunikaty przychodzące do aplikacji (wndclass.lpfnWndProc). Nazwa może być dowolna u nas nazywa się "Pierwszy program", a dlaczego, ano dlatego:

static TCHAR app[] = TEXT( "Pierwszy program" );

Natomiast wndclass.lpszClassName mówi jak nazywa się funkcja, która będzie obsługiwać komunikaty napływające do okna utworzonego na podstawie tej klasy. W naszym przypadku będzie to funkcja MainWndProc, w dalszej części opiszę ją szczegółowo. Kolejne nie mniej ważne, pozwalające zmieniać wygląd okna to:

wndclass.hbrBackground = (HBRUSH)COLOR_WINDOW; - wypełnia okno standardowym kolorem.
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); - wstawia standardową ikonę aplikacji (patrz więcej na temat LoadIcon w helpie)
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); - wstawia standardowy kursor.
wndclass.lpszMenuName = NULL; - pozwala nam dołączyć do programu menu z zasobów. Jak widać nasz program nie posiada menu.

Ostatnią daną składową klasy która jest często używana jest wndclass.style. U nas co prawda ma wartość 0 (NULL), ale np. gdybyśmy chcieli odmalowywać zawartość okna za każdym razem gdy zmieni się jego rozmiar trzeba tam wstawić kombinacje styli CS_HREDRAW | CS_VREDRAW. Będzie to nam potrzebne już przy wyświetlaniu tekstu funkcją DrawText.

Pozostałe są bardzo rzadko używane. Więc jeśli jesteś ciekawy to odsyłam do helpa.

Dobrze wypełniliśmy już klasę. Teraz musimy ją zarejestrować. Robimy to tak:

if(RegisterClass(&wndclass) == 0)

return FALSE;

Jak widać jeśli funkcja rejestrująca zwróci nam 0, będzie to znaczyć tyle, że klasa nie została zarejestrowana. Idzie za tym fakt, że dalej program nie może się wykonywać i zwraca FALSE do głównej funkcji. Bez obaw wszystko powinno przebiec bez takich akcji.

Mamy zarejestrowaną klasę więc możemy zabrać się za budowanie głównego okna programu. Pomocna nam będzie funkcja CreateWindow. Powiem tylko, że to nie jedyna funkcja którą można stworzyć okno. O innych funkcjach tworzących okienka będzie w kolejnych lekcjach. Ale do rzeczy.

hWnd = CreateWindow(app, app, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

Funkcja jako pierwszy argument przyjmuje nazwę zarejestrowanej klasy. Drugi argument to nazwa naszego programu, jak się domyślacie nazywa się tak samo jak klasa, czyli "Pierwszy program". Trzeci parametr funkcji to styl lub ich kombinacja, określająca właściwości naszego okna. W naszym przypadku jest to WS_OVERLAPPEDWINDOW. A to znaczy, że nasze okno będzie miało pasek tytułowy, menu systemowe, ramkę i nagłówek (nazwę na pasku tytułowym). Jednym słowem "normalna aplikacja". Styli jest sporo więc polecam lekturę help'a. Czwarty i piąty argument tej funkcji to odpowiednio x i y współrzędnych lewego górnego rogu naszego okna (w pikselach oczywiście). Kolejne dwa to z kolei rozmiary tworzonego okna liczone od lewego górnego rogu mierzone w prawo i w dół. CW_USEDEFAULT w przypadku argumentów od 4-7 włącznie mówi systemowi aby sam ustalił położenie i rozmiary okna. Kolejny parametr to uchwyt okna nadrzędnego - rodzica, a ponieważ tworzymy okno które jest oknem głównym i nie ma żadnych okien nadrzędnych, ten parametr ma ustawiony NULL. Kolejny parametr jest identyfikatorem menu lub gdy okno jest "dzieckiem" może zawierać identyfikator. Szczegółowo omówię to innym razem. Przedostatni parametr to uchwyt realizacji do naszej aplikacji. I ostatni argument raczej rzadko używany jest wskaźnikiem do pewnej struktury, którą na początku nie powinieneś zawracać sobie głowy, a ustawiać ten parametr jako NULL.

Teraz tylko sprawdzamy czy na pewno okno zostało utworzone, a jak to sprawdzić

if(hWnd == NULL)
return FALSE;

Wiadomo jeśli uchwyt (pamiętamy, że to liczba) równy jest zero, to nici z dalszego działania programu, bo okno nie zostało stworzone.

Myślicie, że jak już stworzyliście okno, to od razu pojawi się na ekranie. Właśnie, że nie. Musimy je do tego zmusić.

ShowWindow(hWnd, SW_SHOW);

Powyższa funkcja, jak jej nazwa wskazuje, pokazuje nam stworzone okno na ekranie monitora. Pierwszym argumentem jest uchwyt okna, które ma być pokazane, a drugi argument wskazuje w jakiej postaci ma zostać wyświetlone okno na ekranie - w naszym przypadku będą to rozmiary początkowe okna (te ustawione przez Windows).

Okno jest już na ekranie, ale nic w nim nie ma. Zawartość okna została wypełniona (zamalowana) pędzlem - tym utworzonym w klasie tego okna. Aby w oknie pokazało się to chcemy, np. tekst, grafika itp. Trzeba wywołać komunikat WM_PAINT (o komunikatach czytaj niżej).

UpdateWindow(hWnd);

Ta funkcja ma wysoki priorytet więc nie będzie czekać w kolejce, aż być może inne komunikaty zostaną przetworzone.

Teraz jest dobry moment aby, przedstawić w jaki sposób działa aplikacja w systemie Windows. Gdy uruchamiamy program i utworzone zostanie okno, zostaje wysłany pierwszy komunikat, jest nim WM_CREATE. Tak jest w naszym przypadku. Komunikat może nazywać się inaczej np. gdy tworzymy okno z zasobów. Niezależnie od tego aplikacja działa na zasadzie "AKCJA - REAKCJA", a ściślej mówiąc na zasadzie wysłany komunikat i reakcja na niego lub ignorowanie go. Spytacie pewnie czym jest ten komunikat. Komunikat jest strukturą zdefiniowaną w pliku nagłówkowym WINUSER.H:

typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG, *PMSG;

Zawiera następujące pola:

hwnd - uchwyt okna, do którego skierowany jest komunikat.
message - identyfikator komunikatu. Jest to liczba, dla ułatwienia programowania zdefiniowano jednak identyfikatory słowne, które możemy rozpoznać po przedrostku WM_ ("Window message").
wParam i lParam - to 32 bitowe parametry komunikatu, które są zależne od rodzaju komunikatu. Mówiąc najprościej zawierają informację konkretne "co się stało".
time - czas w którym komunikat został umieszczony w kolejce komunikatów (wywołany).
pt - współrzędne pozycji kursora w momencie wywołania komunikatu.

Komunikaty odgrywają dużą rolę w programowaniu w WinAPI więc radzę zapamiętać przynajmniej 4 pierwsze pola struktury komunikatu. Będzie nam to za chwilę przydatne do zrozumienia działania programu.

Jak pewnie zauważyliście nie skończyliśmy jeszcze interpretacji zawartości funkcji WinMain. A mamy tam jeszcze takiego stwora ;-)

while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

Jak pewnie wiecie jest to zwykła instrukcja warunkowa while. Jak wynika z tej instrukcji, jeśli funkcja GetMessage zwróci wartość niezerową pętla będzie wykonywana. Przyjrzyjmy się jednak bliżej tej funkcji.

GetMessage(&msg, NULL, 0, 0)

Funkcja pobiera komunikat z kolejki komunikatów. Pierwszy parametr to już chyba wiecie, tak to struktura komunikatu. Drugi to uchwyt okna, jeśli ustawimy NULL to komunikaty będą pobierane dla wszystkich okien danej aplikacji. Trzeci i czwarty żąda pobrania wszystkich komunikatów. Funkcja zwróci NULL, jeśli otrzyma komunikat WM_QUIT.

Dalej mamy TranslateMessage. Argumentem jest wskaźnik do struktury komunikatu. Funkcja przesyła ten wskaźnik do systemu w celu przetworzenia komunikatów klawiatury.

Następnie wskaźnik do struktury przesyłany jest do procedury okna u nas jest to MainWndProc, którą zaraz omówię, a robi to funkcja:

DispatchMessage(&msg);

Tak więc wiemy, że pętla jest wykonywana do póki nie otrzyma komunikatu WM_QUIT. Wtedy zostanie zwrócona wartość do funkcji WinMain i program się kończy, w praktyce wygląda to tak:

return msg.wParam;

Chyba wiadomo jaka to wartość. Jest to pole wParam struktury komunikatu msg.

Teraz omówię procedurę okna o której wcześniej wspominałem. Procedura okna, to po prostu funkcja MainWndProc nazwę funkcji możemy oczywiście zmienić na dowolną. W naszym programie zdefiniowana jest następująco:

LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

Pierwszy parametr tej funkcji to uchwyt okna które otrzymało komunikat. My na razie stworzyliśmy jedno okno, ale można stworzyć ich o wiele więcej, ale o tym innym razem. Drugi parametr pobiera liczbę identyfikującą parametr, czyli drugie pole struktury komunikatu (message). Trzeci i czwarty parametr pobiera dodatkowe informacje o komunikacie, zależne od typu komunikatu. Dalej w funkcji mamy coś takiego:

switch (uMsg)
{

case WM_CREATE:

break;

case WM_DESTROY:

PostQuitMessage(0);
break;

case WM_KEYDOWN:

break;

Na początku mamy instrukcję switch. Sprawdza ona czy został wywołany komunikat. Pierwszy komunikat jaki mamy obsługiwany to WM_CREATE. Ten komunikat wywoływany jest podczas tworzenia okna. Następnym jest WM_DESTROY podczas tego komunikatu kończymy nasz program wysyłając komunikat WM_QUIT. Służy do tego funkcja PostQuitMessage, której argument zwracany jest do funkcji WinMain i program kończy swoje działanie. Kolejnym komunikatem jest WM_KEYDOWN, który jak można się domyślić jest wywoływany podczas wciśnięci klawisza. Obsługa tego komunikatu opisałem w lekcji 5.

Dalej mamy:

default:

return (DefWindowProc(hWnd, uMsg, wParam, lParam));

Jeśli został wywołany komunikat, którego nie obsługujemy, system Windows "obsłuży" ten komunikat za nas. Co w większości przypadków oznacza, że go zignoruje.

I na końcu stoi

return(0L);

Kończy działenie a funkcja okienkowa i zwraca 0.

< Poprzedni   Następny >
Ankieta
Jak ocenisz poziom swoich umiejętnośći?
  
Dodaj do ulubionych
Ustaw stronę startową
Ostatnio dodane
Popularne
 
top

www.winapi.org © 2003 - 2007