Tutorial 3: Jednoduché okno

V tomto tutoriálu postavíme Windows program, které zobrazí plně funkční okno na ploše.
Stáhněte si kód z: here

Theory:

Programy ve Windows se hutně spoléhají na API funkce pro jejich GUI (grafické rozhraní). To má výhody jak pro uživatele, tak pro programátory. Pro uživatele tu výhodu, že se nemusí učit zacházet s GUI každého programu zvlášť, GUI programů pod Windows je podobné pro každý program. Pro programátory tu, že GUI kódy jsou již hotové, otestované a připravené k používání. Nevýhodou pro programátory jsou však zvýšené nároky na komplexnost jejich znalostí. Abychom mohli vytvořit nebo manipulovat s jakýmkoliv GUI objektem, jako jsou okna, menu nebo ikony, musíme dodržovat určitý striktní postup. Ale toto může být překonáno modulárním programováním nebo paradigmatem OOP (objektově orientované programování).
Nyní načrtnu kroky potřebné k vytvoření okna na ploše:
  1. Získat handle instance našeho programu. (vyžadováno)
  2. Získat obsah příkazové řádky (není vyžadováno, pokud nepotřebujeme zpracovávat příkazovou řádku)
  3. Registrovat třídu okna (vyžadováno, pokud neužíváte předdefinované typy okna, jako např.. MessageBox nebo dialog box)
  4. Vytvořit okno (vyžadováno)
  5. Ukázat okno na ploše (vyžadováno, ledaže nechcete, aby se okno hned objevilo)
  6. Obnovit klientskou oblast okna
  7. Vstoupit do nekonečné smyčky, kontrolujíc zprávy od Windows.
  8. Jestliže zprávy dorazí, jsou zpracovány specializovanou funkcí , která je zodpovědná za okno.
  9. Ukončit program jestliže uživatel zavře okno
Jak jste si jistě všimli, struktura programu ve Windows je dosti komplexní ve srovnání s programem v DOSu. Ale svět Windows je podstatně odlišný od světa DOSu. Programy pod Windows musejí být schopny v míru spoluexistovat. Musí proto dodržovat přísnější pravidla. Vy, jakožto programátor musíte také být přísnější na svůj programovací styl a návyky.

Obsah:

Pod námi je kód našeho jednoduchého okna. Předtím, než se však vrhneme na detaily Win32 ASM programování, doporučím některé body, které vám programování ulehčí. .386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib            ; volání k funkcím v user32.lib a kernel32.lib
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD

.DATA                     ; inicializovaná data
ClassName db "SimpleWinClass",0        ; jméno naší třídy okna(window class)
AppName db "Naše první okno",0        ; jméno našeho okna

.DATA?                ; neinicializovaná data
hInstance HINSTANCE ?        ; handle instance našeho programu
CommandLine LPSTR ?
.CODE                ; tady začíná náš kód
start:
invoke GetModuleHandle, NULL            ; získej handle instance našeho programu.
                                                                       ; pod Win32, hmodule==hinstance mov hInstance,eax
mov hInstance,eax
invoke GetCommandLine                        ; Získej obsah příkazové řádky. Nemusíte tuto funkci volat, jestliže
                                                                       ; příkazovou řádku zpracovávat nechcete.
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT        ; volej hlavní funkci
invoke ExitProcess, eax                           ; ukonči program. Exit kód je vrácen v eax od WinMain.

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
    LOCAL wc:WNDCLASSEX                                            ; vytvoříme lokální proměnné v zásobníku
    LOCAL msg:MSG
    LOCAL hwnd:HWND

    mov   wc.cbSize,SIZEOF WNDCLASSEX                   ; naplníme hodnoty členů struktury wc
    mov   wc.style, CS_HREDRAW or CS_VREDRAW
    mov   wc.lpfnWndProc, OFFSET WndProc
    mov   wc.cbClsExtra,NULL
    mov   wc.cbWndExtra,NULL
    push  hInstance
    pop   wc.hInstance
    mov   wc.hbrBackground,COLOR_WINDOW+1
    mov   wc.lpszMenuName,NULL
    mov   wc.lpszClassName,OFFSET ClassName
    invoke LoadIcon,NULL,IDI_APPLICATION
    mov   wc.hIcon,eax
    mov   wc.hIconSm,eax
    invoke LoadCursor,NULL,IDC_ARROW
    mov   wc.hCursor,eax
    invoke RegisterClassEx, addr wc                       ; registrujeme naší třídu okna
    invoke CreateWindowEx,NULL,\
                ADDR ClassName,\
                ADDR AppName,\
                WS_OVERLAPPEDWINDOW,\
                CW_USEDEFAULT,\
                CW_USEDEFAULT,\
                CW_USEDEFAULT,\
                CW_USEDEFAULT,\
                NULL,\
                NULL,\
                hInst,\
                NULL
    mov   hwnd,eax
    invoke ShowWindow, hwnd,CmdShow               ; zobrazí naše okno na ploše
    invoke UpdateWindow, hwnd                                 ; obnoví klientskou oblast

    .WHILE TRUE                                                         ; vstoupíme do smyčky zpráv
                invoke GetMessage, ADDR msg,NULL,0,0
                .BREAK .IF (!eax)
                invoke TranslateMessage, ADDR msg
                invoke DispatchMessage, ADDR msg
   .ENDW
    mov     eax,msg.wParam                                            ; vrátí exitový kód v eax
    ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    .IF uMsg==WM_DESTROY                           ; jestli uživatel zavře naše okno
        invoke PostQuitMessage,NULL             ; ukončím aplikaci
    .ELSE
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam     ; standardní(Default) zpracování zpráv
        ret
    .ENDIF
    xor eax,eax
    ret
WndProc endp

end start

Analýza:

Možná jste docela překvapeni, že jednoduché okno vyžaduje tolik kódování. Ale většina toho kódu jsou vlastně jen šablony, které můžete kopírovat z jednoho zdrojového kódu do druhého. Nebo pokud chcete, můžete část kódu zkompilovat do knihovny a využívat ji na začátku a konci svých programů jako prolog a epilog. Kód můžete psát pouze ve WinMain funkci. Ve skutečnosti tohle dělají C compilátory. Nechají vás psát kód WinMain funkce aniž by jste se museli starat o další detaily. Jediný háček je v tom, že musíte mít funkci pojmenovanou WinMain, jinak C kompilátory nebudou schopny kombinovat váš kód s se svým úvodním(prologue) a konečným(prologue) kódem. V assembleru takové omezení nemáte. Můžete použít libovolné jméno namísto WinMain nebo nakonec nemusíte použít žádnou hlavní funkci.
Připravte se. Tohle bude dlouhý, předlouhý tutorial. Rozeberme program k smrti :)! První tři řádky jsou "nutnosti". .386 říká MASM, že máme v úmyslu užívat 80386 instrukční sadu v našem programu. .model flat,stdcall říká MASM, že užíváme flat paměťový model. Taktéž použijeme stdcall konvenci jako základní v našem programu.
Následuje prototyp funkce WinMain. Poněvadž budeme volat WinMain později, musíme její prototyp definovat dříve než použijeme invoke.
Musím zahrnout windows.inc na začátku kódu. Obsahuje důležité struktury a konstanty. Hlavičkový soubor windows.inc je pouhý textový soubor. Otevřete ho libovolným textovým editorem. Prosím uvědomte si, že windows.inc neobsahuje ještě všechny strukutry a konstanty. Spolu s Hutchem na tom pracujeme. Sami si můžete přidat další položky, jesliže tam nejsou.
Náš program volá API funkce, které přebývají v user32.dll (CreateWindowEx, RegisterWindowClassEx, např.) a kernel32.dll (ExitProcess), takže musíme spojit náš program s těmito knihovnami. Další otázka: Jak mám poznat, která knihovna by měla být slinkována s mým programem ? Odpověď: Musíte vědět, kde volané API funkce přebývají. Když např. zavoláte API funkci v gdi32.dll, musíte linkovat s gdi32.lib.
To je přístup MASM. Přístup TASM je mnohem jednodušší: nalinkujte jen jeden jediný soubor: import32.lib. Další jsou datové sekce.
V .DATA deklarujeme dva nulou ukončené řetězce, (ASCIIZ řetězce): ClassName, což je jméno třídy našeho okna a AppName, což je jméno našeho okna. Všimněte si, že tyto proměnné jsou inicializovány.
V .DATA?, deklarujeme dvě proměnné: hInstance (handle instance našeho programu) a CommandLine (příkazová řádka našeho programu). Neznámé data typy, HINSTANCE a LPSTR jsou ve skutečnosti jen jiná jména pro DWORD. Můžete si je vyhledat ve windows.inc. Všimněte si také, že proměnné v .DATA? sekci nejsou inicializována, což znamená, že neobsahují žádnou konkrétní hodnotu při startu, ale chceme pro ně zatím rezervovat místo pro další použití. .CODE obsahuje všechny naše instrukce. Kód musí být mezi <starting label>: a end <starting label>. Název labelu není důležitý. Můžete si ho pojmenovat jak chcete, jen musí být jedinečný a neporušovat konvenci pojmenování MASM.
Naší první instrukcí je volání funkce GetModuleHandle pro získání handelu instance našeho programu. Pod Win32, handle instance a handle modulu jsou jedno a to samé. Můžete brát handle instance jako ID našeho programu. Je užíván jako parametr několika API funkcemi, které musíme volat, takže je obecně dobrým nápadem získat ho na začátku programu.
Poznámka: Ve skutečnosti je pod Win32 handle instance lineární adresou našeho programu v paměti.
Při návratu z Win32 funkce, je návratová hodnota, pokud je tedy nějaká, uložena v registru eax. Všechny případné ostatní hodnoty jsou vráceny přes proměnné předané v seznamu parametrů, který definujete pro volání.
Win32 funkce si téměř vždy uchovají segmentové registry, a ebx, edi, esi a ebp registry. Naproti tomu jsou ecx a edx pomocné registry a jsou vždy nedefinovány při návratu z Win32 funkce.
Poznámka: Neočekávejte, že registry eax, ecx, edx budou obsahovat stejné hodnoty po návratu z API funkcí.
Závěr je tento: Když voláte API funkci, čekejte návratovou hodnotu v eax. Jestliže jakákoliv vaše funkce bude volána Windows, musíte také hrát podle pravidel. Uchovejte a poté před návratem obnovte hodnoty segmentových registrů, ebx, edi, esi and ebp, jinak váš program rychle spadne, to vše platí i pro vaši proceduru okna a callback funkce.
Volání GetCommandLine funkce není nutné, jestliže váš program nebude pracovat s příkazovou řádkou. V tomto případě jsem to jen ukázal pro případ, že by jste to někdy potřebovali.
Další je volání WinMain. Zde přijímá čtyři parametry. Handle instance programu, handle instance předchozí instance našeho programu, příkazovou řádku a stav okna při prvním spuštění. Pod Win32 neexistuje žádná předchozí instance. Každý program je ve svém adresovém prostoru, takže hodnota hPrevInst je vždy 0. To je ještě pozůstatek z dob Win16, kdy všechny instance programu běžely ve stejném adresovém prostoru a instance chtěli znát, jestli jsou první instancí. Pod win16, jesliže hPrevInst byla NULL, pak byla tato instance první.
Poznámka: Nemusíte deklarovat jméno hlavní funkce jako WinMain. Ve skutečnosti máte uplnou svobodu v tomto smyslu. Nemusíte dokonce ani použít podobnou funkci jako WinMain a můžete zkopírovat kód uvnitř WinMain za GetCommandLine a program i tak poběží korektně.
Při návratu z WinMain je eax naplněn únikovým(exitovým) kódem, který předáme jako parametr funkci ExitProcess, která ukončí aplikaci.

WinMain proc Inst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD

Řádek výše je deklarace funkce WinMain. Všimněte si párů parametr:typ, které následují po PROC direktivě. To jsou parametry, které WinMain obdrží od volajícího. Můžete se odkazovat na tyto parametry jménem na místo manipulace se zásobníkem. K tomu ještě MASM vygeneruje prologue a epilogue kód, takže se nemusíme zabývat balancováním zásobníku při vstupu a ukončení funkce.

    LOCAL wc:WNDCLASSEX
    LOCAL msg:MSG
    LOCAL hwnd:HWND

LOCAL direktiva alokuje pamět ze zásobníku pro lokální proměnné použité ve funkci. Svazek LOCAL direktiv musí být hned pod PROC direktivou. Za LOCAL direktivou hned následuje <jméno lokální proměnné>:<typ proměěné >. Takže LOCAL wc:WNDCLASSEX říká MASM, aby alokoval pamět ze zásobníku o velikosti WNDCLASSEX struktury pro proměnnou jménem wc. Můžeme se na tuto proměnnou odkazovat bez jakýchkoliv obtíží s manipulací se zásobníkem. To je vskutku Boží dar, řekl bych. Nevýhodu je, že lokální proměnné nemůžeme použít vně funkce, protože jsou tvořeny a budou zničeny automaticky, když se funkce vrací k volajícímu. Další nevýhodou je, že nemůžete inicializovat lokální proměnné automaticky, protože jsou vlastně jen dynamicky alokovanou částí paměti zásobníku při vstupu do funkce. Musíte jim ručně přiřadit požadované hodnoty až po LOCAL direktivách.

    mov   wc.cbSize,SIZEOF WNDCLASSEX
    mov   wc.style, CS_HREDRAW or CS_VREDRAW
    mov   wc.lpfnWndProc, OFFSET WndProc
    mov   wc.cbClsExtra,NULL
    mov   wc.cbWndExtra,NULL
    push  hInstance
    pop   wc.hInstance
    mov   wc.hbrBackground,COLOR_WINDOW+1
    mov   wc.lpszMenuName,NULL
    mov   wc.lpszClassName,OFFSET ClassName
    invoke LoadIcon,NULL,IDI_APPLICATION
    mov   wc.hIcon,eax
    mov   wc.hIconSm,eax
    invoke LoadCursor,NULL,IDC_ARROW
    mov   wc.hCursor,eax
    invoke RegisterClassEx, addr wc

Ony řádky nad námi jsou koncepčně jednoduché. Konceptem těchto řádků je   třída okno (window class). Window class není nic jiného víc než plánem nebo specifikací okna. Definuje několik důležitých charakteristik okna jako jsou jeho ikona, kursor, funkci odpovědnou za okno, jeho barvu apod. Vytvoříte okno z třídy Okno (window class). To je objektově orientovaný koncept. Jestliže chcete vytvořit další okno se stejnými vlastnostmi, je rozumné uložit všechny tyto vlastnosti na jedno místo a v případě potřeby ho využít. Tímto lze ušetřit dost paměti a vyhnout se tím zbytečným zdvojováním informací. Uvědomte si, že Windows bylo designováno v minulosti, kde paměťové čipy byly cenově nepřístupné a většina počítačů měla 1 MB paměti. Windows musejí být velice efektivní v použítí paměťových zdrojů. Pointa je tato: Jestliže si definujete vlastní okno, musíte vyplnit požadované vlastnosti vašeho okna ve WNDCLASS nebo WNDCLASSEX struktuře a zavolat funkci RegisterClass nebo RegisterClassEx předtím, než budete moci vaše okno vytvořit. Musíte registrovat třídu Okno jednou pro každý typ okna, ze kterého chcete tvořit okno.
Windows má několik předdefinovaných tříd Okno, jako jsou buttony a editboxy. Pro tyto okna (nebo jiný název controls), nemusíte registrovat další třídu Okno, pouze zavoláte CreateWindowEx s předdefinovaným názvem třídy.
Jednoduše nejdůležitějším členem WNDCLASSEX struktury je lpfnWndProc. lpfn značí long pointer to function (daleký ukazatel na funkci). Pod Win32 již nejsou žádné "near" - blízké nebo "far" - daleké ukazatele, jednoduše kvůli FLAT paměťovému modelu. Ale znovu to je pouze pozůstak z dob Win16. Každá třída Okno musí být asociována s funkcí, která se nazývá window procedure (procedura okna). Tato procedura je zodpovědná za práci se zprávami všech oken vytvořených z asociované třídy Okno. Windows posílá zprávy proceduře okna, aby ji upozornil na důležité události, které se týkají oken, za které je procedura odpovědná, jako je uživatelův vstup z klávesnice nebo myši. Je jen na proceduře okna, aby inteligentně reagovala na každou událost, kterou obdrží ke každému oknu. Většinu svého času budete trávit psaním kódu na obsluhování událostí v proceduře okna.
Popíšu každý člen WNDCLASSEX:

WNDCLASSEX STRUCT DWORD
  cbSize            DWORD      ?
  style             DWORD      ?
  lpfnWndProc       DWORD      ?
  cbClsExtra        DWORD      ?
  cbWndExtra        DWORD      ?
  hInstance         DWORD      ?
  hIcon             DWORD      ?
  hCursor           DWORD      ?
  hbrBackground     DWORD      ?
  lpszMenuName      DWORD      ?
  lpszClassName     DWORD      ?
  hIconSm           DWORD      ?
WNDCLASSEX ENDS

cbSize: Velikost WNDCLASSEX struktury v bytech. Můžeme použít operátor SIZEOF pro její získání.
style: Styl oken vytvořených z tého třídy. Můžete kombinovat několik stylů pomocí "or" operátoru.
lpfnWndProc: Adresa procedury okna, která je zodpovědná za okna vytvořené z této třídy.
cbClsExtra: Specifikuje počet extra bytů k alokaci pro další window-class strukturu. Operační systém inicializuje tyto byty nulou. Zde můžete uložit data specifická pro třídu Okno.
cbWndExtra: Specifikuje počet extra bytů k alokaci další instance okna. Operační systém inicializuje tyto byty nulou. Jestliže aplikace používá WNDCLASS strukturu k registraci dialog boxu vytvořeného pomocí CLASS direktivy v resource souboru, musí nastavit tento člen na DLGWINDOWEXTRA.
hInstance: Handle instance modulu.
hIcon: Handle k ikoně. Dostaneme ho funkcí LoadIcon.
hCursor: Handle ke kurzoru. Dostaneme ho funkcí LoadCursor.
hbrBackground: Barva pozadí oken vytvořených z třídy.
lpszMenuName: Nastavený handle k menu pro okna vytvořená z třídy.
lpszClassName: Jméno této třídy.
hIconSm: Handle k malé ikonce, která je spojena s třídou. Jestliže je tento člen NULL, systém prohledá zdroje ikony specifikované členem hIcon pro ikonu odpovídající velikosti.

    invoke CreateWindowEx, NULL,\
                                                ADDR ClassName,\
                                                ADDR AppName,\
                                                WS_OVERLAPPEDWINDOW,\
                                                CW_USEDEFAULT,\
                                                CW_USEDEFAULT,\
                                                CW_USEDEFAULT,\
                                                CW_USEDEFAULT,\
                                                NULL,\
                                                NULL,\
                                                hInst,\
                                                NULL

Po registraci třídy Okno, můžeme volat CreateWindowEx pro vytvoření našeho okna založené na naší třídě. Všimněte si, že tahle funkce má 12 parametrů.

CreateWindowExA proto dwExStyle:DWORD,\
   lpClassName:DWORD,\
   lpWindowName:DWORD,\
   dwStyle:DWORD,\
   X:DWORD,\
   Y:DWORD,\
   nWidth:DWORD,\
   nHeight:DWORD,\
   hWndParent:DWORD ,\
   hMenu:DWORD,\
   hInstance:DWORD,\
   lpParam:DWORD

Pojďme si prohlédnout detaily každého parametru:
dwExStyle: Extra styl okna. Tohle je nový parametr, který byl přidán ke starému CreateWindow. Zde můžete vložit nové styly pro Windows 95 & NT. Běžné styly můžete specifikovat v dwStyle, ale jestli chcete nějaké speciální efekty, musíte je specifikovat zde. V případě, že extra styl nechcete, použijte NULL.
lpClassName: (Vyžadováno). Adresa ASCIIZ řetězce obsahujícího jméno třídy okna, které chcete použít jako šablonu pro toto okno. Třída může být vaše vlastní registrovaná nebo předdefinovaná. Jak řečeno výše, každé okno, které vytvoříte musí být založeno na třídě Okno.
lpWindowName: Adresa ASCIIZ řetězce obsahujícího jméno okna. Bude zobrazeno v titulku okna. Jestliže je tento parametr NULL, pak bude titulek prázdný.
dwStyle:  Styly okna. Zde můžete určit vzhled okna. Předáním NULL  je v pořádku, ale okno nebude mít system menu box, žádné tlačítka pro minimalizaci a maximalitaci, a žádné tlačítko pro zavření. Okno pak vlastně nebude k použítí :). Potřebovali by jste Alt+F4 na zavření. Nejběžnější styl okna je WS_OVERLAPPEDWINDOW. Styl okna je vlastně pouze stav jednoho bitu. Proto můžete jednotlivé styly kombinovat logickým operátorem "or" pro požadovaný vzhled. WS_OVERLAPPEDWINDOW styl je ve skutečnosti kombinací nejběžnějších stylů.
X,Y: Souřadnice horního levého rohu okna. Normálně by tyto hodnoty měly být CW_USEDEFAULT, tj. Windows si rozhodnou, kam okno umístí na plochu.
nWidth, nHeight: Šířka a výška okna v pixelech. Můžete také použít CW_USEDEFAULT a nechat tím Windows vybrat vhodnou šířku a výšku za vás.
hWndParent: Handle k (parent) rodičovskému oknu našeho okna (pokud existuje). Tento parametr říká Windows, zda-li je toto okno potomkem-child (podřízené) nějaké jiného okna a pakliže ano, které okno je rodičovské. Toto ale není vztah rodič-potomek v MDI (multiple document interface). Podřízená okna nejsou vázána ke klientské oblasti okna rodičovského. Tento vztah je čistě pro interní potřeby Windows. Jestliže je rodičovské okno zavřeno, všechna podřízená okna budou také zavřena. Opravdu je to tak snadné. Protože je ale v našem příkladu jen jedno okno, použijeme NULL.
hMenu: Handle k menu okna. NULL pokud použijeme základní menu třídy. Podívejte se zpět na člena WNDCLASSEX struktury, lpszMenuName. lpszMenuName specifikuje *standardní* menu pro třídu okna. Každé okno vytvořené z této třídy bude mít standardně stejné menu. Pokud menu "nepřepíšete" pro konkrétní okno díky jeho hMenu parametru. hMenu je ve skutečnosti parametr pro dva účely. V případě, že okno, které chcete vytvořit je předdefinovaného typu (tzv. control), nemůže vlastnit menu. hMenu je pak použito jako ID controlu. Windows může rozhodnout, zda-li je hMenu skutečně handle k menu nebo právě jen ID tím, že se podívá na lpClassName parametr. Jestliže je jím jméno předdefinované třídy okna, pak je hMenu pouze ID. Pokud ne, pak je to handle k menu okna.
hInstance: Handle instance programu vytvářejícího okno.
lpParam: Volitelný ukazatel na datovou strukturu předávaný oknu. Ten užívá MDI okno pro předání CLIENTCREATESTRUCT dat. Za normálních okolností je NULL, což znamená, že přes CreateWindow() nepředáváme žádná data. Okno může získat hodnotu tohoto parametru voláním funkce GetWindowLong.

    mov   hwnd,eax
    invoke ShowWindow, hwnd,CmdShow
    invoke UpdateWindow, hwnd

Při úspěšném návratu z CreateWindowEx je handle k oknu vrácen v registru eax. Tuto hodnotu si musíme uchovat pro pozdější použití. Okno, které jsme právě vytvořili není automaticky zobrazeno. K tomu musíme zavolat ShowWindow s handelem okna a požadovaným stavem zobrazení. Potom zavoláme UpdateWindow, abychom přebarvili klientskou oblast okna. Tato funkce je užitečná, když chcete aktualizovat obsah klientské oblasti. Můžete ale tuto funkci vynechat.

    .WHILE TRUE
                invoke GetMessage, ADDR msg,NULL,0,0
                .BREAK .IF (!eax)
                invoke TranslateMessage, ADDR msg
                invoke DispatchMessage, ADDR msg
   .ENDW

Naše okno je již vidět na obrazovce. Ale nemůže ještě registrovat vstupy uživatele ze světa. Takže musíme nějak zařídit, aby naše okno mohlo zpracovávat události, které se ho týkají. To učiníme pomocí tzv. smyčky zpráv (message loop). Pro každý modul je pouze jedna smyčka zpráv. Tato smyčka opakovaně kontroluje zprávy od Windows pomocí GetMessage. GetMessage předává ukazatel na MSG structure Windowsům. Tato MSG struktura bude naplněna informacemi o zprávě, kterou chce Windows poslat oknu v modulu. GetMessage funkce se nevrátí, dokud ještě existují zprávy pro okno. Během této doby může Windows předat kontrolu jiným programům. Tohle je právě to co vytváří spolupracující víceúlohové schéma platformy Win16. GetMessage vrací FALSE když obdrží zprávu WM_QUIT, která ve smyčce zpráv preruší smyčku a tím se program ukončí.
TranslateMessage je funkce, která bere čistý vstup z klávesnice a generuje novou zprávu (WM_CHAR), která je vložena do fronty zpráv. Zpráva WM_CHAR obsahuje ASCII hodnotu zmáčknuté klávesy, s čímž se pracuje snadněji než s čistými scan kody kláves. Můžete tuto funkci vynechat, pokud nechcete zpracovávat stisky kláves.
DispatchMessage posílá data zprávy proceduře okna, která je zodpovědná za zprávy určitého okna. is for.

    mov     eax,msg.wParam
    ret
WinMain endp

Jestliže se smyčka zpráv přeruší, exitový kód je uložen ve wParam členu MSG struktury. Můžete tento kód uložit do eax a vrátit ho Windows. V současnosti ho sice Windwos již nevyužívá, ale pro jistotu je lepší hrát podle pravidel.

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

Tohle je naše procedure okna. Nemusíte jí pojmenovat WndProc. První parametr hWnd je handle okna, pro které je zpráva určena. uMsg je zpráva. Všimněte si, že uMsg není MSG struktura. Je to opravdu jen člen. Windows definují stovky zpráv, jejichž většinu vaše programy nebudou zajímat. Windows pošlou vhodnout zprávu oknu v případě, že stane něco, co má s oknem spojitost. Procedura okna pak zprávu přijme a inteligentně na ni reaguje. wParam a lParam jsou parametry navíc, které užívají některé zprávy. Některé zprávy ještě posílají určitá související data. Tyto data jsou předávány proceduře okna právě pomocí lParam and wParam.

    .IF uMsg==WM_DESTROY
        invoke PostQuitMessage,NULL
    .ELSE
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .ENDIF
    xor eax,eax
    ret
WndProc endp

Teď přichází klíčová část. Zde je místo, kde přebývá většina inteligence vašeho programu. Kód, který reaguje na každou zprávu Windows je v proceduře okna. Musíte ověřit, jestli zpráva od Windows je ta, která vás zajímá. Jestliže ano, dělejte cokoliv jako reakci na tu zprávu a pak se vraťte s nulou v eax. Pakliže vás zpráva nezajímá, MUSÍTE zavolat  DefWindowProc a předat všechny parametry, které jste obdržely standardnímu(default) zpracování. DefWindowProc je právě API funkce, která zpracovává zprávy, které vás nezajímají
Jediná zpráva, na kterou MUSÍTE odpovědět, je WM_DESTROY. Tuto zprávu obdrží procedura okna, když bylo okno zavřeno. V době, kdy procedura okna tuto zprávu obdrží již bylo vaše okno odstraněno z obrazovky. Je to jen upozornění, že vaše okno bylo zavřeno a že by jste se měli připravit na návrat do Windows. Jako odpověď můžete provést úklid před návratem do Windows. Nemáte jinou možnost, než končit když dojde na tuto zprávu. Jestliže chcete mít příležitost zastavit uživatele od zavření okna, měli by jste zpracovat WM_CLOSE zprávu. Nyní zpět k WM_DESTROY, po vykonání úklidových prací musíte volat PostQuitMessage, která pošle WM_QUIT zprávu zpět vašemu modulu. WM_QUIT donutí GetMessage k návratu s nulou v eax, což má následek přerušení smyčky zpráv a návrat do Windows. Zprávu WM_DESTROY můžete poslat vaší vlastní proceduře okna voláním DestroyWindow funkce.


[Iczelion's Win32 Assembly HomePage]
Překlad Shaldan 2006, fx.s@seznam.cz - při četbě a využívání nových poznatků z četby opravdu za nic neručím :))