Nadszedł czas na stworzenie okna w naszym programie, czyli w funkcji WinMain. Nie jest to skomplikowane, ale dość długie w zapisie, dlatego nie przestraszcie się, jak zobaczycie długi kod.

Musimy mieć świadomość, że nasz program to proces, który może stworzyć własne okno i sterować nim, nasze okno nie jest programem, ale tylko elementem procesu. Każdy proces może stworzyć wiele różnych okien i wszystkie te okna, będą sterowane z jednego programu(procesu).

Klasa okna

Ale nie wszystko na raz, zacznijmy od początku. Najpierw musimy uzupełnić strukturę o nazwie WNDCLASSEX. Jest to struktura zawierająca informacje o klasie naszego okna. Spójrzmy na poniższy program:

#include <windows.h>

INT WINAPI WinMain(HINSTANCE hInst,HINSTANCE,LPSTR lStart,INT nShow)
{
  WNDCLASSEX wc;
//deklarujemy naszą strukturę klasy okna o nazwie wc
  //po zadeklarowaniu uzupełniamy ją informacjami

  wc.hInstance=hInst;
//uchwyt naszego procesu programu

  wc.lpszClassName=
"Nazwa klasy okna"
;
//nazwa klasy naszego okna

  wc.lpfnWndProc=DefWindowProc;
//procedura obsługi dla klasy okna

  wc.style=CS_DBLCLKS;
//styl klasy okna

  wc.cbSize=sizeof(WNDCLASSEX);
//wielkość naszej klasy okna w pamięci
  //wygląd okna

  wc.hIcon=LoadIcon(
0
,IDI_APPLICATION);
//ikona okien

  wc.hIconSm=LoadIcon(
0
,IDI_APPLICATION);
//mała ikona okien

  wc.hCursor=LoadCursor(
0
,IDC_ARROW);
//kursor

  wc.lpszMenuName=
0
;
//menu w oknach utworzonych z tej klasy

  wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
//tło okna
  //dodatkowe dane

  wc.cbWndExtra=
0
;
  wc.cbClsExtra=
0
;
  
//rejestracja klasy w systemie

  if(RegisterClassEx(&wc)==
0
) return
0
;
  
//kontynuacja programu

  return
0
;
}

Jak już wcześniej powiedziałem, aby mieć okno, musimy stworzyć klasę onka WNDCLASSEX w naszym programie. Każde okno w windowsie musi należeć do jakiejś klasy okien, czyli takiego szablonu mówiącego jak okno ma się zachowywać. W swoim programie klasę okna nazwałem "wc". Po zadeklarowaniu klasy okna, możemy zająć się wypełnianiem danych w naszej klasie okna. Kolejno omówię elementy w strukturze WNDCLASSEX. Oto jej ciało:

struct WNDCLASSEX
{
  HINSTANCE hInstance;
  LPCSTR lpszClassName;
  WNDPROC lpfnWndProc;
  UNIT style;
  UNIT cbSize;
  HICON hIcon;
  HICON hIconSm;
  HCURSOR hCursor;
  LPCTSTR lpszMenuName;
  HBRUSH hbrBackground;
  INT cbWndExtra;
  INT cbClsExtra;
}

HINSTANCE hInstance
Jest to uchwyt do procesu, do którego ma należeć ta klasa. My oczywiśćie chcemy, żeby klasa należała do naszego programu, więc tej zmiennej przypisujemy uchwyt naszego programu, który pochodzi z pierwszego parametru naszej funkcji WinMain.

LPCSTR lpszClassName
Jest to nazwa naszej klasy okna, można ją nazwać jak tylko chcemy. Każda klasa okien w systemie musi mieć swoją unikatową, niepowtarzalną nazwę i tu ją ustalamy.

WNDPROC lpfnWndProc
Jest to nazwa funkcji, która będzie obsługiwać komunikaty przychodzące do okien utworzonych z tej klasy. O pisaniu takich funkcji będzie po następnej lekcji. Narazie przypiszmy tej klasie, domyślną funkcję "DefWindowProc", która wszystkie komunikaty obsługuje domyślnie za nas.

UNIT style
Tutaj przypisujemy styl okien, utworzonych z tej klasy. Możemy nie podawać domyślego stylu, przypisując 0. Przydatnym stylem może być CS_DBLCLKS, dzięki temu stylowi nasze okna utworzone z tej klasy, mogą obsługiwać podwójne kliknięcie. Więcej stylów poznasz w kolejnej lekcji.

UNIT cbSize
Tutaj przypisujemy wielkość naszej klasy w pamięci. Należy użyć operatora sizeof, aby uzyskać wilekość struktury WNDCLASSEX.

HICON hIcon
Jest to uchwyt ikony dla naszych okien utworzonych z tej klasy. Rozmiar tej ikony powinien wynosić co najmniej 32x32 piksele. Zwykle ikona ta jest wyświetlana tylko w pasku, który pojawi się po naciśnięciu Alt+Tab. Uchwyt można uzyskać używając funkcji LoadIcon. Jeżeli używamy standardowej systemowej ikony, pierwszego parametru nie podajemy(0), natomiast drugi ma być numerem systemowej ikony. Oto kilka najpopularnieszych ikon systemowych: IDI_APPLICATION, IDI_QUESTION, IDI_ASTERISK, IDI_HAND, IDI_EXCLAMATION, IDI_WINLOGO(tylko w win95/98). Więcej na temat ikon będzie w innej lekcji.

HICON hIconSm
Jest to uchwyt do małej ikony okna. Te mniejsze ikony są pokazywane na paskach tytułowych w oknach utworzonych z tej klasy, oraz na pasku zadań Windows. Zwykle tu podajemy uchwyt do tej samej ikony co hIcon lub podejemy 0, co także oznacza użycie uchwytu z hIcon. Nawet gdy ikony nie będą się zgadzać swoimi rozmiarami z zalecanymi, Windows i tak odpowiednio przeskaluje ikony i dopasuje je do swoich wymagań.

HCURSOR hCursor
Uchwyt kursora na okna utworzone z tej klasy. Uchwyt możemy uzyskać używając funkcji LoadCursor. Podobna jest ona do funkcji LoadIcon, ale ta służy do załadowania kursora. W przypadku standardowego systemowego kursora, należy pominąc(podać 0) pierwszy argument, natomiast w drugim podać numer kursora. Standardową strzałką jest IDC_ARROW.

LPCTSTR lpszMenuName
Jest to nazwa zasobu menu w oknach utworzonych z tej klasy. Na temat robienia menu będzie w innej lekcji. Poza tym, menu na oknie możemy umieścić jeszcze w innym miejscu.

HBRUSH hbrBackground
Jest to uchwyt do pędzla z tłem okna. Do uzyskania takiego uchwytu możemy użyć funkcji GetStockObject, jako parametr przyjmuje ona gotowy pędzel. Funckję należy rzutować na obiekt typu HBRUSH(pędzel). Przykładowe pędzle: BLACK_BRUSH, WHITE_BRUSH, COLOR_BTNFACE. Więcej na ten temat pędzli w dziale poświęconym GDI.

INT cbWndExtra
Określa ilość dodatkowej pamięci w bajtach, dla każdego okna utworzonego z tej klasy. Zwykle dodatkowa ilość nie jest potrzebna.

INT cbClsExtra
Określa ilość dodatkowej pamięci w bajtach dla naszej klasy. Oczywiście dodatkowa pamięć nie jest nam potrzebna.

Po wypełnieniu klasy należy ją zarejestrować w systemie, aby Windows widział, że została utworzona nowa klasa okna w naszym programie. Do rejestracji służy Funkcja RegisterClassEx. Jako parametr przyjmuje adres struktury klasy, którą będziemy rejestrować, adres uzyskujemy poprzez użycie operatora "&". Zwraca ona 1, jeżeli rejestracja się powiedzie, natomiast 0 jeżeli się nie powiedzie.

Utworzenie okna

Mamy już klasę naszego okna, ale to dopiero pół sukcesu, dopiero teraz możemy utworzyć nasze okno w pamięci. Aby utworzyć okno potrzeba nam będzie nowa zmienna HWND. Jest to właśnie uchwyt naszego okna, czyli jego numer, po którym będziemy mogli się do niego odwoływać. Teraz wystarczy stworzyć okno funkcją CreateWindowEx.

#include <windows.h>

CONST CHAR ClassName[]=
"Klasa naszego okna"
;
//deklaracja globalnej nazwy naszej klasy, lepiej tak robić


INT WINAPI WinMain(HINSTANCE hInst,HINSTANCE,LPSTR lStart,INT nShow)
{
  
//struktura klasy okna

  WNDCLASSEX wc;
  wc.hInstance=hInst;
  wc.lpszClassName=ClassName;
//używamy globalnej nazwy dla naszej klasy

  wc.lpfnWndProc=DefWindowProc;
  wc.style=CS_DBLCLKS;
  wc.cbSize=sizeof(WNDCLASSEX);
  wc.hIcon=LoadIcon(
0
,IDI_APPLICATION);
  wc.hIconSm=
0
;
  wc.hCursor=LoadCursor(
0
,IDC_ARROW);
  wc.lpszMenuName=
0
;
  wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
  wc.cbWndExtra=
0
;
  wc.cbClsExtra=
0
;
  if(RegisterClassEx(&wc)==
0
) return
0
;
//rejestracja klasy w systemie

  
//tworzenie okna o nazwie Okno przy uzyciu funkcji CreateWindowEx

  HWND Okno=CreateWindowEx(
0
,ClassName,
"Tytuł okna"
,WS_OVERLAPPEDWINDOW,
50
,
50
,
100
,
100
,
0
,
0
,hInst,
0
);
  return
0
;
}

Typ HWND to uchwyt okna naszego programu, jak pamiętasz, jest to numer okna w systemie. Po deklaracji uchwytu okna należy je zainicjować. Do tworzenia okna(inicjacji), służy funkcja CreateWindowEx. Funkcja pobiera 12 parametrów okna. Tak wygląda jej deklaracja:

HWND CreateWindowEx
(
  DWORD dwExStyle,
//rozszerzony styl okna

  LPCTSTR lpClassName,
//nazwa klasy okna do jakiej ma należeć okno

  LPCTSTR lpWindowName,
//tytuł okna, będzie wyświetlany na pasku tytułu

  DWORD dwStyle,
//podstawowy styl okna

  INT x,
//współrzędna x okna w pikselach

  INT y,
//współrzędna y okna w pikselach

  INT nWidth,
//szerokość okna w pikselach

  INT nHeight,
//wysokość okna w pikselach

  HWND hWndParent,
//uchwyt okna rodzica, ma być to główne okno, naszym rodzicem będzie pulpit, więc podajemy 0

  HMENU hMenu,
//uchwyt do menu okna, drugi sposób zrobienia menu, o którym wcześniej wspominałem

  HINSTANCE hInstance,
//uchwyt procesu do którego ma należeć nasze okno, podajemy tu uchwyt swojego programu

  LPVOID lpParam
//wskaźnik na dodatkowe informacje, które zostaną przekazane z komunikatem tworzącym okno

);

DWORD dwExStyle
Styl rozszerzony okna, o stylach będzie w następnej lekcji.

LPCTSTR lpClassName
Nazwa klasy do jakiej ma należeć okno. Tutaj powinniśmy podać nazwę klasy, którą przed chwilą utworzyliśmy dla naszego programu. To co napisaliśmy w wc.lpszClassName piszemy także tutaj.

LPCTSTR lpWindowName
Tytuł okna, który będzie widoczny na pasku tytułowym okna. Istnieje możliwość poźniejszej zmiany funkcją SetWindowText.

DWORD dwStyle
Podstawowy styl okna, o stylach będzie w następnej lekcji.

INT x
Współrzędna x okna na pulpicie(w pikselach) lub oknie rodzica.

INT y
Współrzędna y okna na puplicie(w pikselach) lub oknie rodzica.

INT nWidth
Szerokośc okna(w pikselach).

INT nHeight
Wysokość okna(w pikselach).

HWND hWndParent
Uchwyt okna rodzica, czyli okna z którego utworzone jest okno. Jeżeli tworzymy normlane okno w programie(takie które znajduje się na pulpicie) należy podać 0(lub HWND_DESKTOP), rodzicem takiego okna będzie domyślnie pulpit. Jeżeli podamy tu uchyt do innego okna, to nasze okno zostanie utworzone wewnątrz tego podanego okna.

HMENU hMenu
Menu okna. Podajemy 0, bo nie mamy menu. O tworzeniu menu będzie w następnych lekcjach.

HINSTANCE hInstance
Uchwyt procesu do którego ma należeć okno. Oczywiste jest, że podajemy numer naszego programu hInst.

LPVOID lpParam
Dodatkowe dane, których nie mamy.

Po wywołaniu funckcji inicjującej okno(CreateWindowEx), zwróci ona uchwyt do nowo utworzonego okna. Jeżeli fucnkja zwróci pusty uchwyt(0), oznacza to, że coś się nie powiodło.

Pokazanie okna na ekranie

Mamy już zainicjowane okno w pamięci. Można skompilować projekt i zobaczymy, że nadal nic się nie pokazuje. Okno, które utworzyliśmy, mamy w pamięci, jednak aby je zobaczyć musimy je pokazać. Możemy to zrobić na 2 sposoby:

1) Poprzez wywłanie funkcji ShowWindow.

Po utworzeniu okna funkcją CreateWindowEx, używamy funkcji ShowWindow. Oto jej deklaracja:

INT ShowWindow
(
  HWND hWnd,
//uchwyt modyfikowanego okna

  INT Show
//rodzaj stanu widoczności okna

);

Nazwa funkcji ShowWindow(), jest trochę myląca, gdyż nie służy jedynie pokazywaniu okna, ale zmienianiu stanu widoczności tego okna. Pierwszym parametrem jest uchwyt okna, które modyfikujemy, natomiast drugim sposób wyświetlenia okna. Oto kilka możliwości wyświetlenia okna:
SW_SHOW - pokazanie okna w normalnych rozmiarach,
SW_HIDE - schowanie okna(nie mylić ze zminimalizowaniem), jest ono nie widoczne z poziomu użytkownika, domyślnie każde okno po uzyciu funkcji CreateWindowEx, ma ten status,
SW_MAXIMIZE - okno jest zmaksymalizowane,
SW_MINIMIZE - okno jest zminimalizowane,
SW_RESTORE - przywrócenie oknu standardowych rozmiarów(tych z fucnkji CreateWindowEx).

Aby okno było widoczne należy nadać mu stan widoczności(SW_SHOW), wywołujemy fucnkje:

...
ShowWindow(Okno,SW_SHOW);
//pokazanie okna

...

Oczywiście dzięki tej funkcji możemy także schować pokazywane okno, czy zmaksymalizować lub zminimalizować:

...
ShowWindow(Okno,SW_HIDE);
//schowanie okna

...

...
ShowWindow(Okno,SW_MAXIMIZE);
//zmaksymalizowanie okna

...

...
ShowWindow(Okno,SW_MINIMIZE);
//zminimalizowanie okna

...

Tutaj trzeba też powiedzieć o ostatnim parametrze funkcji WinMain, otóż jest to właśnie ten sposób wyświetlenia okna, tyle, że to system przekazuje sposób wyświetlenia, zwykle jest to SW_SHOW. Ja osobiście radzę właśnie stosować wyświetlanie według tego parametru WinMain, ponieważ nasz program będzie bardziej powiązany z systemem. No chyba, że już bardzo chcemy sami określić w jaki sposób ma nam się pokazać okno.

#include <windows.h>

CONST CHAR ClassName[]=
"Klasa naszego okna"
;

INT WINAPI WinMain(HINSTANCE hInst,HINSTANCE,LPSTR lStart,INT nShow)
{
  WNDCLASSEX wc;
  wc.hInstance=hInst;
  wc.lpszClassName=ClassName;
  wc.lpfnWndProc=DefWindowProc;
  wc.style=CS_DBLCLKS;
  wc.cbSize=sizeof(WNDCLASSEX);
  wc.hIcon=LoadIcon(
0
,IDI_APPLICATION);
  wc.hIconSm=
0
;
  wc.hCursor=LoadCursor(
0
,IDC_ARROW);
  wc.lpszMenuName=
0
;
  wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
  wc.cbWndExtra=
0
;
  wc.cbClsExtra=
0
;
  if(RegisterClassEx(&wc)==
0
) return
0
;
  HWND Okno=CreateWindowEx(
0
,ClassName,"Tytuł okna",WS_OVERLAPPEDWINDOW,
50
,
50
,
100
,
100
,
0
,
0
,hInst,
0
);
  ShowWindow(Okno,nShow);
//pokazujemy okno, używamy ostatniego parametru z funkcji WinMain

  return
0
;
}

2) Poprzez dodanie stylu WS_VISIBLE.

Innym sposobem pokazanie okna jest dodanie stylu WS_VISIBLE w funkcji CreateWindowEx. Ten styl powoduje, że odrazu po zainicjowaniu okno przyjmie status SW_SHOW(widoczny). Styl ten dodaje się w czwartym parametrze funkcji, łączy się go z istniejącymi używając operatora sumy bitowej |. Takie okno zawsze po utworzeniu będzie widoczne.

#include <windows.h>

CONST CHAR ClassName[]=
"Klasa naszego okna"
;

INT WINAPI WinMain(HINSTANCE hInst,HINSTANCE,LPSTR lStart,INT nShow)
{
  WNDCLASSEX wc;
  wc.hInstance=hInst;
  wc.lpszClassName=ClassName;
  wc.lpfnWndProc=DefWindowProc;
  wc.style=CS_DBLCLKS;
  wc.cbSize=sizeof(WNDCLASSEX);
  wc.hIcon=LoadIcon(
0
,IDI_APPLICATION);
  wc.hIconSm=
0
;
  wc.hCursor=LoadCursor(
0
,IDC_ARROW);
  wc.lpszMenuName=
0
;
  wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
  wc.cbWndExtra=
0
;
  wc.cbClsExtra=
0
;
  if(RegisterClassEx(&wc)==
0
) return
0
;
  HWND Okno=CreateWindowEx(
0
,ClassName,"Tytuł okna",WS_OVERLAPPEDWINDOW|WS_VISIBLE,
50
,
50
,
100
,
100
,
0
,
0
,hInst,
0
);
  
//w czwartym parametrze jest dodany styl WS_VISIBLE

  return
0
;
}

Teraz po skompilowaniu projektu, w którym mamy pokazane okno, na moment powinniśmy zobaczyć mignięcie naszego okna.