Вы находитесь на странице: 1из 87

Win32asm

Lekcija 1: Osnove
Za pracenje ovih lekcija je potrebno osnovno poznavanje MASM assemblera. U slucaju da niste upoznati sa njim
downloadujte win32asm.exe i proucite tekst iz paketa pre nego sto nastavite sa ovim lekcijama.
Teorija:
Win32 programi se izvrsavaju u posebnom zasticenom modu koji postoji jos od 80286 vremena. 80286 je zastareo
procesor koji se vise ne koristi tako da nas interesuju samo 80386 i noviji. Windows kao operativni sistem izvrsava
svaki program u odvojenom virtuelnom prostoru sto znaci da svaki program ima svoj sopstveni prostor od 4GB.
Ovo ipak ne znaci da svaki Win32 program ima na raspolaganju 4GB fizicke memorije vec da ima mogucnost da radi
u jednom okruzenju gde se adrese lokacija u memoriji krecu u tom rasponu. Windows ce uraditi sve sto je potrebno da
bi adresa kojoj pristupamo bila vazeca ali naravno program mora da se povinuje pravilima operativnog sistema jer ce u
suprotnom izazvati poznati General Protection Fault (GPF).
Svaki program je sam u svom virtuelnom prostoru sto je suprotno onome sto smo imali na Win16. U 16-bitnom
operativnom sistemu je svaki program mogao da "vidi" bilo koji drugi, sto nije moguce u Win32. Ovo je dobro jer
umanjuje sanse da jedan program pise preko informacija ili instrukcija drugog programa.
Organizacija memorije se takodje promenila od 16-bitnih dana. Pod Win32 vise ne morate da se brinete o segmentima!
Postoji samo jedan "memorijski model" zvani "flat memory model". Vise ne postoje segmenti od 64K vec jedan
prostor od 4GB. Ovo zaci da vise nema potreba da se igrate sa segment registrima tako da se mozete direktno odnositi
na bilo koji deo memorije sto je OGROMNA olaksica programerima i to je bas ono sto cini Win32 assembly
prgramiranje lako kao C.
Kada programirate za Win32 morate da znate neka osnovna pravila. Jedno od takvih pravila je to da Windows koristi
registre esi, edi, ebp i ebx i da ocekuje da se njihove vrednosti ne menjaju. Zato zapamtite ovo prvo pravilo: ako zelite
da koristite neki od ovi registara u nekoj svojoj porvratnoj ("callback") funkciji onda nemojte da zaboravite da pre
izlaska iz funkcije povratite vrednosti tih registara. Povratna fukcija je neka vasa funkcija ciju ste adresu negde dali
Windowsu pa je on pokrece. Jedan klasican primer takve funkcije je funkcija koja prima i odgovara na sve Windowsove poruke upucene odredjenom prozoru, popularno zvana "window procedure". Ovo nikako ne znaci da ne mozete da
koristite gore navedene registre vec samo da morate da osigurate da ce njihova vrednost pri izlasku iz funkcije biti ista
kao i na ulasku u nju.
Sadrzaj:
Evo skeleta jednog programa. Ne brinite ako ne razumete nesto od koda. Malo kasnije cu objasniti svaki deo
pojedinacno.
.386
.MODEL Flat, STDCALL
.DATA
<Ne inicijalizovan data segment>
......
.DATA?
<Data segment sa inicijalizovanim vrednostima>
......
.CONST
<Konstante>
......
.CODE
<etiketa>
<Programski kod>
.....
end <etiketa>
To je sve! Ajmo da sada analiziramo ovaj skelet.
.386
Ovo je komanda naseg assemblera koja mu govori da koristi istrukcije procesora 80386. Pored nje mozete da koristite
i .486, .586 ali je najsigurnije da se ostane pri .386. Pored ovoga postoje i dva skoro identicna moda u obliku

.386/.386p, .486/.486p itd.


Te "p" verzije su neophodne samo kada program koristi priviligovane ("privileged") instrukcije. Priviligovane
instrukcije su instrukcije rezervisane od strane procesora ili operativnog sistema kada se nalazi u zasticenom
("protected") modu i mogu se koristiti jedino od strane drajvera. U vecini slucajeva vas ce program raditi u ne
priviligovanom modu tako da se slobodno mogu koristiti modeli bez "p".
.MODEL FLAT, STDCALL
.MODEL je assemblerova komanda koja mu govori koji ce memorijski model da se koristi. Pod Win32 jedino postoji
FLAT model.
STDCALL govori assembleru o nacinu prosledjivanja parametara funkcijama. Nacin prosledjivanja je redosled
prosledjiuvanja parametara koji moze da bude od levo ka desno ili od desno ka levo i takodje vodi racuna o stacku
posle pozivanja funkcije.
Pod Win16 postoje dva nacina prodledjivanja parametare: C i PASCAL
C nacin prosledjuje parametre od desno ka levo, tj. poslednji parametar se prvi smesta na stack. Onaj ko poziva
funkciju je odgovoran za stack, na primer, da bi pozvao funkciju foo(int parametar_1, int parametar_2, int
parametar_3) u C nacinu prosledjivanja u assembly-u bi uradio sledece:
push [third_param] ;Stavi treci parametar na stack
push [second_param] ;Pracen drugim
push [first_param] ;A zatim prvi
call foo
add sp,12
;Nakon poziva cemo izbalansirati stack
PASCAL nacin prosledjivanja je suprotan od C nacina prosledjivanja. Parametri su prosledjeni od levo ka desno i
pozvana funkcija je odgovorna za stack.
Win16 je prihvatio PASCAL nacin prosledjivanja jer je tako dobijen kod manji. C nacin je koristan kada ne znas
koliko parametara ce biti prosledjeno jednoj funkciji kao na primer u slucaju wsprint(). U tom primeru ne postoji nacin
za funkciju da unapred odredi broj parametara ce biti smesten na stack tako da i ne mozda da ga izbalansira.
STDCALL je hibrid C i PASCAL nacina prosledjivanja. Prosledjuje parametre od desno ka levo ali je pozvana
funkcija odgovorna za balansiranje stacka posle poziva. Win32 platforma ekskluzivno koristi STDCALL . Osim u
jednom slucaju: wsprintf(). Morate koristiti C nacin prosledjivanja sa wsprintf().
.DATA .DATA? .CONST .CODE
Sve cetiri direktive su zvane sekcija. Secate se, u Win32 ne postoje segmenti? Ali mozete podeliti celokupan prostor
od 4GB na logicke sekcije. Start jedne sekcije pretstavlja i kraj prethodne. Postoje dve grupe sekcija: informacije
(data) i insrukcije (code). Informacije su podeljene u 3 kategorije:
.DATA Ova se sekcica sastoji promenjivih sa vec dodeljenom vrednoscu.
.DATA? Ova sekcija sadrzi promenjive bez dodeljene vrednosti. Ponekad samo zelite da imate nesto vezane
("allocated") memorije ali ne zelite da ima bilo kakvu pocetnu vrednost. Ova sekcija sluzi bas tome. Prednosti
ove sekcije su da ne zauzima nikakv prostor u samom EXE fajlu. Na primer, ako vezete 10,000 bajtova u
.DATA? sekciji vas EXE nece porasti za 10,000 bajtova vec ce velicina ostati prakticno ista, jedino ce assembler
znati koliko ekstra memorije ce trebati da se veze kada se program ucita u memoriju i to je sve.
.CONST Ova sekcija sadrzi deklaracije svih konstantnih vrednosti koje koristi program. Konstante ne mogu
nikada biti modifikovane od strane programa, one su jednostavno - konstantne.
Ne morate da koristite sve 3 sekcije u vasem programu vec samo one koje zelite da koristite.
Postoji samo jedna sekcija za instrukcije: .CODE i to je mesto u kome boravi vas kod.
<etiketa>
end <etiketa>
Ovde je <label> bilo kakva oznaka koja se koristi da se odredi velicina koda. Obe oznake moraju biti identicne i sav
kod mora da bude smesten izmedju njih. <etiketa> i na kraju, end <etiketa>

Lekcija 2: MessageBox
U ovoj lekciji cemo napraviti potpuno funkcionalni Windows program koji ce prikazati poruku "Win32
assembly is great!". Skinite primer ovde . Preptostavlja se da ste iskusni sa MASM assemblerom.
Teorija:
Windows se sastoji od puno funkcija ciju glavnicu cini Windows API (Application Programming Interface).
Windows API je ogromna kolekcija veoma korisnih funkcija koje se nalaze u samom Windowsu i dostupne
su svim programima pisanim za Windows. Sve te funkcije se u stvari nalaze u nekoliko DLL modula kao na
primer u kernel32.dll, user32.dll ili gdi32.dll-u. Kernel 32 dll sadrzi API funkcije koje se koriste u radu sa
memorijom i procesima. User32.dll se bavi korisnickim interfejsom vaseg programa dok se Gdi32.dll koristi
za rad sa grafikom. Postoji jos DLL-ova sto sem ova glavna tri i sve ih mozete koristiti ako imate dovoljno
informacija o zeljenim API funkcijama.
Windows programi se dinamicki povezuju sa ovim DLL-ovima sto znaci da se sam kod ovih funkcija ne
ukljucuje u kod vaseg EXEa po njegovom stvaranju. Da bi program znao gde moze da nadje zeljene API
funkcije za vreme njegovog izvrsavanja, morate da u njega ukljucite i izvesne informacije. Te iformacije su
biblioteke, zvane Import Libraries. Ako ne vezete program sa odgovarajucim bibliotekama on nece moci da
locira API funkcije i samim tim nece biti operativan.
Kada je Windows program ucitan u memoriju Windows cita informacije smestene u programu. Te
informacije izmedju ostalog obuhvataju imena API funkcija koje program poziva kao i DLL kome te funkcije
pripadaju. Kada Windows pronadje te informacije on onda ucita te DLL-ove i "popravi" sve reference ka tim
funkcijama tako da kada program pozove neku od njih tok ode pravo do mesta u memoriji gde se nalazi
odredjena funkcija.
Postoje dve kategorije API funkcija: Jedna je ANSI dok je druga UNICODE. Imena API funkcija pisanih za
ANSI se zavrsavaju sa "A", na primer, MessageBoxA, dok se umena UNICODE funkcija zavrsavaju sa "W"
(skraceno od "Wide Char"). Windows 95 je baziran na ANSI standardu dok je Windows NT na UNICODEu.
Mi smo uglavnom upoznati sa ANSI stringovima koji su uvek jedan skup karaktera okoncan sa jednom NULL
karakterom. ANSI karakter zauzima samo jeda bajt. Dok je ANSI dovoljan za Evropske jezike on ipak ne
moze da obuhvati i nekoliko istocnjackih jezika koji sadrze po nekoliko stotina razlicitih karaktera. Iz tog
razloga je na scenu stupio UNICODE ciji je karakter velicine dva bajta i omogucava nam da imamo do
65536 jedinstvenih karaktera.
U vecini slucajeva cete koristiti odgovarajuce "include" fajlove koji ce da odradjuju koje verzije API funkcija
cete koristiti u zavisnosti za koju platformu pisete program tako da se sve te API funkcije sa A i W
zavrsetcima mozete pozivati bez tih dodatnih karaktera, na primer MessageBoxA mozete pozivati kao
MessageBox.
Osnovni Skelet:
Za sada pogledajmo samo kako izgleda skelet a kasnije cemo ga popuniti.
.386
.model flat, stdcall
.data
.code
start:
end start
Rad programa krece od prve instrukcije koja se nalazi ispod oznake koja prati end direktivu. U ovom skeletu
rad programa pocinje od prve instrukcije odmah ispod startoznake. Izvrsenje ce se linearno nastaviti sve
dok ne dodje na red neka instrukcija koja utice na dalji tok programa, kao na primer jmp, jne, je, ret ili
neka druga. Te funkcije preusmeravaju tok programa na neku drugu instrukciju. Kada program treba da se
okonca onda jednostavno treba da pozove API funkciju ExitProcess.
ExitProcess proto uExitCode:DWORD
Ova linija iznad je bila prototip ili deklaracija funkcije. Ona opisuje funkciju assembleru/linkeru tako da on
moze da izvrsi provere parametara umesto vas. Format jednog prototipa je ovakav:
ImeFunkcije PROTO [NazivParametra]:TipParametra, [NazivParametra]:TipParametra,...
U kratko, ime funkcije prati kljucna rec PROTO a onda lista sa tipom svakog pojedinacnog parametra gde je
svaki tip jednog parametra odvojen zarezom od drugog. U ExitProcess primeru, ExisProcess definise
funkciju koja prima samo jedan parametar tipa DWORD. Prototipovi funkcija su od posebne koristi kada se
koristi makro "invoke" koji olaksava pozivanje funkcija i proverava ono sto smo mi napisali sa onim sto je
definisano u prototipu. Na primer, ako uradite sledece:
call ExitProcess

bez smestanja neke DWORD vrednosti na stack, assembler/linker nece biti u stanju da primeti tu vasu
gresku pa cete vi kasnije pri radu programa primetiti da blokira. Ali, ako koristite:
invoke ExitProcess
Linker ce da vas informise da ste zaboravili da smestite neku DWORD vrednost na stack i time ce spreciti
sve greske takvog tipa. Ja preporucujem koriscenje invoke umesto standardne call instrukcije. Sintaksa
invoke makroa je sledeca:
INVOKE izraz [,argumenti]
izraz moze biti ime funkcije ili pointer ka funkciji. Parametri su razdvojeni zarezima od imena funkcije i
jedan od drugog.
Vecina prototipova API funkcija se nalazu u "include" fajlovima. Ako koristite Hutchov MASM32 paket onda
ih mozete sve naci u \MASM32\Include\ direktorijumu. Include fajlovi imaju ekstenziju .INC i ostatak imena
odgovara imenu DLLa. Na primer, ExitProcess se nalazi u kernel32.dll-u, "izvod" te funkcije se nalazi u
biblioteci kernel32.lib a prototipovi se nalaze u kernel32.inc fajlu.
Mozete takodje da pisete prototipove za vase sopstvene funkcije.
Kroz moje primere cu koristiti Hutchov windows.inc koji mozete skinuti sa http://win32asm.cjb.net
Sto se tice ExitProcess-a, uExitCode parametar je vrednost koju zelite da vas program prosledi Windowsu
po okoncanju programa. Mozete da pozovete ExitProcess ovako:
invoke ExitProcess, 0
Stavite tu liniju direktno ispod start oznake i dobicete Win32 program koji odmah okoncava svoj rad. Koliko
god mali i beskoristan bio, to je ipak jedan funkcionalan Windows program.
Ovo je minimum:
.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
.data
.code
start:
invoke ExitProcess,0
end start
option casemap:none govori MASM-u da se sve oznake razlikuju od drugih slicnih oznaka koje su pisane sa
drugim setom malih ili velikih slova. Na primer, ExitProcess nije isto sto i exitprocess ili EXITprocess.
Zapazite novu direktivu, include . Ova direktiva je pracena imanom fajla koji zelite da bude ubacen na to
specificno mesto. U ranijem primeru kada MASM dodje to linije include \masm32\include\windows.inc on ce
otvoriti windows.inc koji se nalazi u \MASM32\include direktorijumu i obraditi sve konstante nadjene u
njemu kao da ste copy/paste -ovali sadrzaj tog fajla na bas to mesto. Hutchov windows.inc sadrzi puno
konstanti i struktura koje su potrebne kada se pisu Win32 programi ali ne sadrzi prototip ni jedne funkcije.
Windows.inc ni u kom slucaju nije potpun. Hutch se trudi da u njega ubaci sto je vise moguce konstanti i
struktura ali je jos uvek jako veliki broj njoh ostao van. Za najnovije verzije windows.inc fajla sa vremena
na vreme proveravajte moju i Hutchovu stranu.
Kako se u windows.inc ne nalaze definicije funkcija moracete da u program ubacujete i ostale potrebne
include fajlove iz \MASM32\include direktorijuma.
U nasem renijem primeru pozivamo funkciju iz kernel32.dll-a, tako da moramo da ubacimo i definicije
funkcija za kernel32.dll. Fajl koji nam treba je kernel32.inc. Ako otvorite taj fajl sa nekim tekst editorom
videcete da je pun prototipovima funkcija iz kernel32.dll-a. Ako ne ukljucite kernel32.inc mocicete da
pozivate ExitProcess i ostale funkcije ali samo koristeci instrukciju call. Necete moci da invoke funkiju. Ono
sto hocu da kazem je: da bi koristili invoke za neku funkciju morate da negde u kodu imate i prototip
funkcije. U primeru ako ne ubacite kernel32.inc mozete sami definisati ExitProcess negde u kodu i slobodno
koristiti invoke. Include fajlove koristimo da se ne bi gubili vreme sami kucajuci definicije svih tih funkcija
koje cemo koristiti.
Sada se susrecemo sa novom direktivom, includelib . includelib ne radi kao include . To je samo nacin da
kazete assembleru koju biblioteku vas program koristi. Kada assembler vidi includelib direktivu on zapisuje
komandu linkeru negde u object fajl tako da linker zna sa kojim bibliotekama linker mora da spoji vas
program. Ovo mozete uraditi i rucno ali je verujte veoma naporno, a i pored toga moze se pokazati kao
nemoguc zadatak jer komandna linija moze imati samo 128 karaktera.

Sada sacuvajte primer pod imenom msgbox.asm. Pretpostavljajuci da je ml.exe u istom katalogu ili da je u
PATH-u, assemblujte msgbox ovako:
ml /c /coff /Cp msgbox.asm
/c govori MASMu da samo assembluje i ne poziva link.exe. U vecini slucajeva necete zeleti MASM da
poziva automatski link.exe jer cete zeleti da odradite jos neke stvari pre linkera.
/coff govori MASM-u da stvori .OBJ fajl u COFF formatu. MASM koristi varijacuju COFF-a (Common
Object File Format) koji se koristi pod Unixom kao format za object i izvrsne fajlove.
/Cp govori MASM-u da ne dira velika i mala slova. Ako koristite Hutchov MASM32 paket mozete staviti
"option casemap:none" na pocetak koda, ispod .model direktive.
Posto uspesno assemblujete msgbox.asm dobicete msgbox.obj. msgbox.obj je objekat i samo je jedan
korak od konacnog EXE fajla. Sve instrukcije i informacije se nalaze na svom mestu i samo je ostalo da
linker sredi adrese funkcija koje se pozivaju.
Zatim nastavite sa linkom:
link /SUBSYSTEM:WINDOWS /LIBPATH:c:\masm32\lib msgbox.obj
/SUBSYSTEM:WINDOWS obavestava Link o tipu EXEa ovog programa.
/LIBPATH:<path to import library> govori Link-u gde se nalaze biblioteke. Ako koristite MASM32, one ce se
nalaziti u MASM32\lib direktorijumu.
Link cita objekat i popravlja adrese tako da funkcije iz eksternih DLLova ili drugih modula budu dostupne.
Kada se ovaj proces zavrsi rezultat je msgbox.exe.
Sada ste dobili msgbox.exe. Videcete da se nista ne dogadja kada ga pokrenete jer nismo nista stavili u
njega. To je ipak Windows programiranje. Ali pogledaj velicinu tog programa! Na mom kompjuteru on je
svega 1,536 bajta.
Sada cemo da stavimo jedan MessageBox. Prototip te funkcije je sledeci:
MessageBox PROTO hwnd:DWORD, lpText:DWORD, lpCaption:DWORD, uType:DWORD
hwnd je identifikacija (handle) prozora kome ovaj MessageBox pripada. O ovoj identifikaciji (window
handle) mislite kao o jednom broju koji predstavlja odredjeni prozor. Njena vrednost vam nije bitna.
Samo treba da se secate da ona oznacava jedan odredjeni prozor. Da bi bilo sta radili sa nekim
prozorom morate znati njegov handle.
lpText je pointer ka tekstu koji zelite da se pojavi u glavnom delu prozora MessageBox-a. Pointer je u
principu samo adresa necega. Pointer ka stringu je samo adresa prvog bajta tog stringa.
lpCaption je pointer ka naslovu MessageBox-a
uType govori funkciji kakvu ikonu i koje dugmice treba da stavi na prozor
Ajmo da sada ubacimo poziv ka MessageBox-u u program.
A ovo je sta se dobije kada se sve spoji u jednu celinu:
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
.data
MsgBoxCaption db "Iczelion Tutorial No.2",0
MsgBoxText db "Win32 Assembly is Great!",0
.code
start:
invoke MessageBox,NULL, addr MsgBoxText, addr MsgBoxCaption, MB_OK
invoke ExitProcess,NULL
end start
Assemblirajte i pokrenite program. Videcete prozor sa tekstom "Win32 Assembly is Great!".
Ajmo da jos jednom pogledamo kod. Napravili smo dva stringa koja se zavrsavaju sa NULL karakterom u

.DATA sekciji. Secate se da se svaki ANSI string u Windows-u mora zavrsiti sa NULL (0) karakterom.
Koristimo dve konstante, NULL i MB_OK. Ove konstante su definisane u windows.inc tako da umesto da
koristimo neke nerazumljive brojeve samo navodimo imena tih vrednosti sto uvecava citkost koda.
ADDR operator se koristi da bi se dobila adresa neke oznake (labele) ili funkcije i samo radi u konteksu
invoke makroa. Pored ADDR, mozete koristiti i offset koji ima skoro isti efekat. Postoje neke sitne razlike i
jedna od njih je da dok offset moze da radi sa labelama koje su definisane kasnije u kodu, ADDR to ne
moze.
Na primer, ako je neka promenjiva definisana negde nize u izvornom kodu, ADDR jednostavno nece raditi.
invoke MessageBox,NULL, addr MsgBoxText,addr MsgBoxCaption,MB_OK
......
MsgBoxCaption db "Iczelion Tutorial No.2",0
MsgBoxText
db "Win32 Assembly is Great!",0
MASM ce da prijavi gresku. Ako koristite offset umesto ADDR onda ce MASM da odradi posao bez problema.
Druga razlika je da ADDR moze da radi i sa lokalnim promenljivim dok offset to ne moze. Lokalna
promenljiva je samo neki rezervisan prostor na staku i adresa te promenjive je poznata samo za vreme
izvrsavanja programa a ne za vreme assemblovanja. Offset se prevodi za vreme assemblija tako da je
prirodno da ne moze da radi sa lokalnim promenljivim. ADDR je sposoban da prvo proveri da li je
promenljiva lokalna ili ne, tako da ako jeste onda se prevodi u skup instrukcija koje dolaze do adrese
promenljive, a ako nije onda radi identicno offset-u. Evo kako dolazi do adrese lokalne promenljive:
lea eax, LocalVar
push eax

Lekcija 3: Jednostavan Prozor


U ovoj lekciji cemo napraviti Windows program koji prikazuje potpuno funkcionalan prozor na vasoj radnoj
povrsini (desktopu).
Skinite primer ovde.
Teorija:
Windows programi se mahom oslanjaju na API funkcije za svoj graficki interfejs (GUI). Ovaj prilaz je od
koristi kako korisnicima tako i programerima. Za korisnike to znaci da ne mraju da uce kako da koriste
graficki interfejs svakog pojedinacnog programa jer je GUI Windowsa slican u svim programima pisanim za
njega. Za programera to znaci veliku ustedu vremena jer je kod koji se bavi GUIem vec tu, napisan, testiran
i spreman za upotrebu. Losa strana svega toga je sto je taj API kompleksan. Da bi stvarao i radio sa GUI
objektima kao sto su na primer prozori, meniji, ikone i drugo, programer mora da se "strogo drzi recepta",
sto opet moze i da se zaobidje ako se koristi modularno programiranje ili objektno programiranje.
Osnove pravljenja prozora su sledece:
1. Nabavite instance handle programa (neophodno)
2. Dodjite do komandne linije (nije neophodno osim ako program zeli da koristi komandu liniju)
3. Registrujte klasu prozora (neophodno sem u slucaju kada se koristi vec postojec, predefinisan tip
prozora, na primer MessageBox ili dijalog)
4. Napravite prozor (obavezno)
5. Prikazite prozor na desktopu (neophodno osim ako zelimo da prozor na pocetku bude skriven)
6. Obnovite radnu povrsinu prozora
7. Udljite u beskonacnu petlju koja ce da proverava poruke koje Windows salje programu
8. Ako i kada poruka od Windows-a pristigne ona se obradjuje u posebnoj funkciji koja je odgovorna za
odredjeni prozor
9. Ugasite program ako korisnik zatvori prozor
Kao sto mozete da vidite, struktura jednog Windows programa je znatno kompleksnija nego sto je to slucaj
sa jednim DOS programom ali se i svet Windowsa znatno razlikuje od sveta DOSa. Windows programi
moraju biti sposobni da funkcionisu u isto vreme sa drugim Windows programima i da ne ometaju jedni
druge pa zato i moraju da postuju pravila. Vi, kao programer morate takodje biti striktni sa vasim
programskim stilom i navikama.
Sadrzaj:
Ispod se nalazi izvorni kod naseg jednostavnog programa. Pre nego sto uskocimo u sitne detalje
programiranja za Win32 u ASMu moram da vam ukazem na neke stvari sto ce vam olaksati programiranje.
Trebalo bi da sve svoje konstante, strukture i prototipove funkcija ubacite u novi include fajl i njega
ukljucita ne pocetku svog .asm fajla. To ce vas ustedeti dosta truda i gresaka pri kucanju.
Najkompletniji include fajl za MASM32 je trenutno Hutchov windows.inc koji mozete skinuti sa moje
strane. Pored onoga sto ima u njemu vi mozete dodavati i nove konstante i strukture u svoje include
fajlove.
Koristite includelib direktivu koja ce govoriti koja se biblioteka koristi u vasem programu. Na primer,
ako zovete MessageBox trebalo bi da napisete sledece:
includelib user32.lib
na pocetku vaseg .asm fajla. Ova direktiva govori MASMu da ce vas program koristiti funkcije koje se
nalaze u toj biblioteci. Ako vas program koristi funkcije iz vise od biblioteka samo dodajte includelib za
svaku biblioteku koju koristite. Koristeci IncludeLib direktivu necete morati da se brinete o
bibliotekama za vreme linkovanja. Takodje mozete koristiti /LIBPATH parametar linkera koji mu govori
gde moze da nadje biblioteke.
Kada deklarisete protip API funkcije, strkuture ili konstante u vasem include fajlu pokusajte da se
drzite imena koja se koriste i u default windows include fajlovima, ukljucujuci i tip slova (mala/velika).
Ovo ce vas spasiti puno glavobolja kada trazite pomoc oko neke Win32 API funkcije u dokumentaciji.
Koristite jedan batch fajl da automatizujete citav proces assemblovanja. To ce vam ustedeti puno
vremena.
.386
.model flat,stdcall
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib ;pozivi ka funkcijama iz user32.lib i kernel32.lib
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
.DATA ;inicijalizovan data segment;
ClassName db "SimpleWinClass",0 ;ime klase prozora
AppName db "Our First Window",0 ;ime naseg prozora
.DATA? ;ne inicijalizovan data segment
hInstance HINSTANCE ? ;instance handle programa
CommandLine LPSTR ?
.CODE ;ovde pocinje programski kod
start:
invoke GetModuleHandle, NULL ;dodji do instance handle-a naseg programa.
;pod Win32, hmodule==hinstance mov hInstance,eax
mov hInstance,eax
invoke GetCommandLine ;dodji do komandne linije. Ovo vam nije neophodno AKO
;vasem programu nije potrebna komandna linija.
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine,SW_SHOWDEFAULT ;poziv glavnoj funkciji
invoke ExitProcess, eax ;izadji iz programa. izlazni kod je vracen kroz eax, u WinMain.
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX ;napravi lokalne promenljive na stacku
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX ;popuni elemente wc strukture sa vrednostima
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 ;registruj klasu prozora
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 ;prikazi prozor na radnoj povrsini
invoke UpdateWindow, hwnd ;osvezi povrsinu prozora
.WHILE TRUE ;proveravaj poruke u petlji
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam ;vrati izlazni kod u eax
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT,wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_DESTROY ;ako korisnik zatvori nas prozor
invoke PostQuitMessage,NULL ;ugasi program
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam ;predefinisana procedura za obradu poruka
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
Analiza:
Moze vas odbiti cinjenica da je cak i za jedan jednostavan Windows program potrebno dosta koda. Zato je
vazno vec sada napomenuti da je vecina tog neophodnog koda prilicno standardna pa cete je moci kopirati
iz jednog svog starog programa ili ako vam se tako vise svidja mozete da napisete svoju biblioteku cijih ce
par funkcija obavljati sav taj posao u vasim narednim programima, a da na vama ostane da samo napisete
glavni deo. To je u principu ono sto C kompajleri cine. Oni vas puste da pisete kod WinMain procedure i da
ne brinete o drugim stvarima. Jedina mana toga je sto morate imati proceduru zvanu WinMain i nikako
drugacije inace C kompajleri nece moci da kombinuju vas kod sa uvodom i zakljuckom jednog windows
programa. Takvih zabrana nema sa ASM jezikom. Mozete koristiti koje god ime hocete umesto WinMain, a
mozete da i ne koristite nikakvu funkciju.
Pripremite se, ovo ce biti duga, duga lekcija. Ajmo da analiziramo ovaj program do koske!
.386
.model flat,stdcall
option casemap:none WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
Prve tri linije su obavezne. .386 obavestava MASM da nameravamo da koristimo 80386 set instrukcija u
ovom programu. .mode flat,stdcall mu govori da ce nas program da koristi FLAT model memorije i da
koristimo stdcall nacin prosledjivanja parametara kao default.
Sledece je prototip WinMain funkcije. Kako cemo kasnije pozvati WinMain moramo da prvo definisemo
prototip te funkcije da bi posle mogli da je pozovemo koristeci invoke.
Moramo da ubacimo i windows.inc na pocetak izvornog koda. On sadrzi vazne strukture i konstante koje se
cesto koriste u programima. Windows.inc fajl je samo jedan tekstulni fajl i mozete ga otvoriti sa bilo kojim
tekst editorom. Vazno je napomenuti da windows.inc (jos uvek) ne sadrzi sve strukture i konstante i da
Hutch i ja jos uvek radimo na njemu. Vi mozete dodati nove ako se tamo ne nalaze.
Nas program poziva API funkcije koje se nalaze u user32.dll-u (CreateWindowEx i RegisterWindowClassEx
na primer) i u kernel32.dll-u (na primer ExitProcess), tako da moramo da se vezemo i sa tim biliotekama.
Sledece pitanje moze biti: Kako ja mogu da znam koje bi biblioteke trebalo da budu linkovane sam mojim
programom? U tom slucaju bi odgovor bio: Morate nauciti gde se koja API funkcija nalazi. Tako, ako
koristite negu funkciju iz gdi32.dll, morate se linkovati sa gdi32.lib.

Ovo je pristup MASMa. TASMov nacin vezivanja sa ovim tipom biblioteka je znatno jednostavniji: uvek samo
treba linkovati import32.lib i nista vise.
.DATA
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
.DATA?
hInstance HINSTANCE ?
CommandLine LPSTR ?
Sledece na redu su "DATA" sekcije.
U .DATA smo stavili dva stringa koja se zavrsavaju sa NULL karakterom (ASCIIZ stringovi): ClassName koji
je naziv klase naseg prozora i AppName sto je ime naseg prozora. Napomena: ove dve promenjive imaju
dodeljene vrednosti vec u startu.
U .DATA? su takodje samo dve promenljive: hInstance (instance handle naseg programa) i CommandLine
(komandna linija istog). Ovi nepoznati tipovi HINSTANCE i LPSTR su u stvari samo sinonimi za DWORD i
deklarisani su u windiws.inc. Vi mozete koristiti DWORD umesto njih ako vam je lakse. Napomena: sve
promenljive iz .DATA? sekcije nemaju pocetne vrednosti na pocetku vec samo govorimo Windowsu da treba
da rezervise memoriju za njih kada pokrene program.
.CODE
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
.....
end start
.CODE sadrzi sve nase instrukcije. Vas kod se mora nalaziti izmedju <pocetak>: i end <pocetak>. Ime ove
oznake koja je u ovom slucaju "pocetak" nije vazno. Mozete koristiti bilo koje ime sve dok je jedinsveno u
programu i postuje MASMova pravila o nazivanju promenjivih i labela.
Nasa prva instrukcija je poziv GetModuleHandle koji nam daje instance handle naseg programa. Pod Win32,
instance handle i module handle su ista stvar. Mozete misliti o instance handle-u kao o identifikaciji vaseg
programa. Ona se koristi u nekoliko API funkcija koje nas program mora da zove tako da je dobro na
pocetku programa doci do nje i sacuvati je negde.
U stvari, u Win32, instance handle je linearna adresa programa.
Nakon povratka iz Win32 funkcije ona moze vratiti neku vrednost. Ako takve vrednosti ima onda ce ona biti
u eax registru. Sve druge vrednosti su vracene kroz promenljive koje su prosledjene funkciji.
Win32 funkcije koju vi zovete skoro skoro nikada nece menjati vrednosti ebx, edi, esi i ebp registara.
Nasuprot njima, ecx i edx je malo verovatno da ostanu neizmenjeni.
Nemojte ocekivati da vrednosti eax, ecx i edx registara ostanu sacuvane posle API poziva.
Nakon izvrsetka API funkcije ocekujte povratnu vrednostu u eax-u. Ako se bilo koja od vasih funkcija zove
od strane Windowsa onda i vi morate da pratite ovo pravilo: cuvajte i ponovo podesavajte vrednosti ebx,
edi, esi i ebp registare tako da i posle poziva funkcije oni nastave da imaju iste vrednosti kao i pre njenog
poziva. Ako se ne pridrzavate ovoga moze se desiti da vas program blokira. Ovo se odnosti na Window
Procedure funkciju i sve druge callback funkcije.
Poziv ka GetCommandLine nije obavezan ako vas program nece proveravati komandnu liniju. U ovom
primeru sam samo pokazao kako se ta funkcija poziva samo da znate kako se to radi u slucaju da vam je to
potrebno u nekom vasem programu.
Sledeci je poziv ka WinMain. On ovde prima cetiri parametra. Instance handle programa, instance handle
prethodne kopije istog programa, komandnu liniju i stanje prozora pri startovanju. Pod Win32 NE POSTOJI
prethodan instance programa tako da je vrednost hPrevInst uvek 0. Ovo je ostalo uz Win16 dana kada je
koliko god puta ista kopija programa bila pokrenut on bio u istom memorijskom prostoru pa je program
zeleo da sazna da li je prvi ili ne. Pod Win16, ako je hPrevInstance NULL onda nema druge operativne
kopije programa u memoriji.
Funckcija ne mora da se zove WinMain. Imate potpunu slobodu sto se njenog imena tice. Cak ne morate ni
da koristite takvu funkciju vec mozete sav kod iz nje da ubacite do GetCommandLine poziva i sve ce opet
savrseno raditi.

Po povratku iz WinMain, eax sadrzi izlaznu vrednost i mi tu vrednost prosledjujemo ka ExitProcess funkciji
koja gasi nas program.
WinMain proc Inst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
Prethodna linija je protip funkcije WinMain. Zapazite parametre sto prate PROC direktivu. To su parametri
koje prima WinMain i kojima vi mozete da pristupite po imenu tako da nije potrebno da baratate sa
stackom. Pored toga, MASM ce stvoriti neophodan pocetan i zavrsan kod funkcije tako da vi ne morate da
se bavite sa stackom na ulazu i izlazu iz funkcije.
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
Direktiva LOCAL uzima memoriju sa stacka koju ce da koristi kao lokalnu promenljivu. Sve LOCAL direktive
se moraju nalaziti odmah ispod PROC direktive. LOCAL direktiva je pracena sa <ime promenljive>:<tip
promenljive>. Tako LOCAL wc:WNDCLASSEX govori MASMu da veze memoriju sa stacka u velicini
WNDCLASSEX strukture za promenljivu koju nazivamo wc. Na ovaj nacin mozemo raditi sa wc bes teskoca
koje prate manipulaciju stacka. Losa strana ovoga je sto lokalne promenljive nemozemo koristiti van
procedure jer su one stvorene na ulazu u istu i unistene pred izlaz. Druga losa strana je da ne mozete
automatski dati pocetnu vrednost ovim promenljivim zato sto one pretstavljaju samo memoriju sa stacka
koja je dinamicno vezana. Morate rucno dodeliti vrednosti ovim promenljivim.
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
Linije iznad su stvarno jednostavne. Potrebno je samo nekoliko linija instrukcija i koncept iza svih tih linija je
klasa prozora . Window class ili klasa prozora nije nista drugo do skupa osobina odredjenog prozora. Ona
sadrzi par vaznih obelezja prozora kao na primer njegovu ikonu, kursor, funkciju odgovornu za njega, boju
itd. Prozor stvarate iz njegove klase. Ovo je na neki nacin objektno orijentisan koncept. Ako zelite da
stvorite vise od jednog prozora sa istim karakteristikama videcete da je veoma korisno sto sve te
informacije postoje samo na jednom mestu i ne dupliraju se bez razloga. Setitte se da je Windows
dizajniran kada su memorijski moduli imali mali kapacitet i vecina kompjutera je imala samo 1 MB RAMa pa
se memorija morala stedeti. Kada pravite novu klasu za svoj prozor onda morate ispuniti WNDCLASS ili
WNDCLASSEX strukturu i pozvati RegistarClass ili RegisterClassEx pre nego sto nastavite sa kreiranjem
prozora. Svaku novu klasu morate registrovati samo jednom.
U Windowsu postoji par predefinisanih klasa kao sto su na primer BUTTON (dugme) ili EDIT (polje za unos
teksta). Za ove prozore, ili bolje receno kontrole, ne morate registrovati klase vec samo pozvati
CreateWindowEx sa predefinisanim imenom klase.
Najvazniji clan WNDCLASSEX strukture je lpfnWndProc. lpfn oznacava da se radi o pointeru ka funkciji, ili
njenoj adresi. Ovo je "long" pointer jer u Win32 ne postoje "near" ili "far" pointeri kao nekada u DOSu. lpfn
je u principu samo nesto sto nam je ostalo iz Win16 dana. Svaka klasa mora biti vezana sa jednom
funkcijom koja se zove "window procedure". Windows procedure je odgovorna sa reagovanja na poruke
koje su upucene njenom prozoru. Windows ce slati razne poruke ovoj proceduri da bi je obavestilo o
izvesnim dogadjajima koji mogu imati veze sa unosom korisnika preko misa ili tastature. Na window
proceduri se nalazi odgovornost da pravilno odgovori na ove poruke. Vecinu vremena cete provoditi pisuci
akcije koje treba da se izvrse kada se primi neka poruka.
Evo i detaljnijeg opisa svih clanova WNDCLASSEX strukture:
WNDCLASSEX STRUCT DWORD

cbSizeDWORD ?
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: Velicina WNDCLASSEX strukture u bajtovima. Mozemo koristiti SIZEOF operator koji ce nam vratiti
ovu vrednost.
style: Stil prozora koji se stvara koristeci ovu klasu. Mozete kombinovati vise stilova zajedno koristeci "or"
operator.
lpfnWndProc: Adresa window procedure odgovorne za sve poruke koje se salju prozorima koji koriste ovu
klasu.
cbClsExtra: Broj ekstra bajtova koje ce Windows da alocira odmah po zavrsetku Window-Class trukture. Taj
deo memorije ce biti ispuljen nulama i tu vi mozete drzati neke informacije u vezi sa klasom.
cbWndExtra: Broj ekstra bajtova koje ce Windows da alocira nakon instance prozora. Operativni sistem ce
da podesi ove bajtove na nule. Ako program koristi WNDCLASS strukturu da bi registrovao dialog stvoren
koristeci CLASS direktivu u resursu onda on mora podesiti ovaj clan klase na DLGWINDOWEXTRA.
hInstance: Instance handle modula.
hIcon: Identifikacija ikone koju dobijate LoadIcon funkcijom.
hCursor: Identifikacija kursora koju dobijate LoadCursor funkcijom.
hbrBackground: Boja prozora stvorenog koristeci ovu klasu.
lpszMenuName: Identifikacija menija koji ce se koristiti za prozore.
lpszClassName: Naziv klase.
hIconSm: Identifikacija male ikone koja je vezana sa klasom. Ako je ovaj clan NULL sistem ce potraziti malu
ikonu i ikoni datoj u hIcon clanu.
invoke CreateWindowEx, NULL,\
ADDR ClassName,\
ADDR AppName,\
WS_OVERLAPPEDWINDOW,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
NULL,\
NULL,\
hInst,\
NULL
Nakon registrovanja klase pozivamo CreateWindowEx da bi stvorili nas prozor baziran na datoj klasi. Postoji
12 parametara u ovoj funkciji.
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
Ajmo da vidimo opis svih parametara:
dwExStyle: Ekstra stilovi prozora. Ovo je parametar dodat staroj CreateWindow funkciji. Tu mozete staviti
nove stilove prozora za Windows95 & NT.Sada mozete odrediti standardne stilove prozora u dwStyle
parametru ali ako zelite neke od novih, kao na primer prozor koji je iznad svih ostalih onda ih morate ubaciti
u ovaj parametar. Ako je njegova vrednost NULL nece se koristiti ni jedan novi stil.
lpClassName: (Obavezno) Adresa ASCIIZ stringa koji sadrzi ime klase prozora koju zelite da koristite za
prozor. Klasa moze biti neka vasa regisrovana klasa ili jedna od predefinisanih. Svaki prozor koji stvorite
mora biti zasnovan na nekoj klasi.
lpWindowName: Adresa ASCIIZ stringa koji sadrzi ime prozora. Ime prozora ce biti pikazano u njegovoj
naslovnoj liniji. Ako je ovaj parametar NULL onda ce ta linija biti prazna.
dwStyle: Stilovi prozora. Ovde mozete odrediti kako zelite da izgleda prozor. Koriscenje NULL vrednosti je
dozvoljeno ali takav prozor ona nece imati sistemski meni i dugmice na sebi pa cete morati da koristite
ALT+F4 da bi ga zatvorili. Najprimenjeniji stil prozora je WS_OVERLAPPEDWINDOW. Stil prozora je u stvari
samo skup prekidaca koji zauzimaju jedan bit u celoj 32bitnoj vrednosti tako da ih mozete kobinovati
koristeci "or" operator. WS_OVERLAPPEDWINDOW je kombinacija vise stilova koji se cesto koriste.
X,Y: Koordinate gornjeg levog ugla prozora. Ova ce vrednost biti CW_USEDEFAULT ako zelite da Windows
odlucuje o tome gde ce se pojaviti prozor.
nWidth, nHeight: Sirina i visina prozora u pixelima. Mozete takodje koristiti CW_USEDAFAULT koji ce
prepustiti Windowsu da izabere odgovarajucu sirinu i visinu za vas.
hWndParent: Identifikacija (handle) prozora-roditelja ukoliko jedan postoji. Ovaj parametar govori Windows
da je novi prozor dete nekog postojeceg prozora koji time postaje njegov roditelj. Napomena: ovo nije
odnost roditelj-dete kao u slucaju MDI interfejsa. Dete jednog prozora nije po defaultu vezano za radni
prostor roditelja vec se ta veza koristi od strane samog Windows-a za neke njegove stvari. Kada je jedan
prozor zatvori automatski ce se zatvoriti i sva njegova deca-prozori. Kako je nas prozor samo jedan i ne
zelimo da bude dete bilo kog drugog prozora mi cemo za ovaj handle dati NULL vrednost.
hMenu: Identifikacija menija ovog prozora. Koristite NULL ako ce se koristiti meni odrednjen u klasi prozora
(Pogledajte WNDCLASSEX strukturu po lpszMenuName). lpszMenuName je default meni za tu klasu. Svaki
prozor stvoren sa tom klasom ce imati isti meni po defaultu osim ako se ovo ne preinaci preko hMenu
parametra. hMenu se koristi za jos jednu stvar: u slucaju da je prozor koji stvaramo kontrola onda prozor
ne moze imati svoj meni i parametar se koristi kao identifikacioni broj kontrole. Windows moze da odredi da
li je hMenu identifikacija kontrole ili menija tako sto ce da pogleda lpClassName parametar. Ako je ime klase
jedno od predefinisanih onda ce hMenu uzeti za ID kontrole, a u suprotnom ce ga smatrati za ID menija tog
prozora.
hInstance: Identifikacija programa koji stvara prozor.
lpParam: Opcioni pointer ka informacijama koje prosledjujemo prozoru. Ovo koriste MDI prozori da bi dosli
do CLIENTCREATESTRUCT informacija. Uglavnom cete ovaj parametar ostavljati NULL sto znaci da nikakve
informacije nece biti proslednjene CreateWindow funkciji. Prozor moze da dodje do ovih informacija
koristeci GetWindowLong funkciju.
mov hwnd,eax
invoke ShowWindow, hwnd,CmdShow
invoke UpdateWindow, hwnd
Po uspesnom povratku iz CreateWindowEx funkcije dobicete identifikaciju novog prozora u eax registru.
Morate da cuvate ovaj broj za kasniju upotrebu. Prozor ovako stvoren nije odmah prikazan i morate pozvati
ShowWindow sa identifikacijom prozora i zeljenim stanjem prozora kako bi on bio prikazan. Zatim pozivate
UpdateWindow da bi Windows iscrtao i time obnovio radnu povrsinu prozora. Ovaj poziv je koristan kada
zelite da se u datom trenutku prozor ponovo iscrta ali ga sada mozete i izostaviti.
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
Sada se prozor vec nalazi na ekranu ali on ne moze da prima nikakav unos sto znaci da ga mi moramo
informisati o svim relevantnim dogadjajima. Ovo se radi koristeci jednu petlju koja radi sa porukama. Postoji
samo jedna ovakva pelja u svakom modulu. Ova petlja koristi GetMessage da non-stop proverava poruke
sto Windows salje prozoru. GetMessage daje windowsu pointer ka MSG strukturi koja ce biti ispunjena sa

informacijama o poruci koju je Windows poslao. GetMessage se nece okoncati dok ne dodje do neke
poruke. Za to vreme Windows moze da prepusti kontrolu drugim programima i to cini multitasking semu
Win16 platforme. GetMessage vraca FALSE ako je primila WM_QUIT poruku. Tada treba okoncati petlju i
izaci iz programa.
TranslateMessage je pomocna funkcija koja uzima unos iz tastature i stvara novu poruku - WM_CHAR koja
je onda smestena u listu poruka upucenih prozoru. Poruka sa WM_CHAR sadrzi i ASCII vrednost stisnutog
tastera sto je mnogo lakse za koriscenje nego scan kodovi. Ova poruka ne mora da se koristi ako program
ne koristi unos sa tastature.
DispatchMessage salje poruku window proceduri odgovornoj za odredjeni prozor.
mov eax,msg.wParam
ret
WinMain endp
Kada se petlja okonca izlazna vrednost ne nalazi u wParam clanu MSG strukture. Ovu vrednost mozete
smestiti u eax tako da se ona vrati Windowsu. Windows trenutno ne koristi ovu vrednost ali je bolje igrati
na sigurno i povinovati se pravilima.
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
Ovo je nasa window procedura. Ne morate koristiti WndProc naziv. Prvi parametar, hWnd je identifikacija
prozora kome je upucena ova poruka. uMsg je sama poruka. uMsg nije MSG struktura vec samo jedan broj.
Windows ima na stotine poruka koje nas uglavnom i ne zanimaju. Operativni sistem salje odgovarajuce
poruke kada se nesto dogodi datom prozoru, tu poruku prima window precedura i odgovara na nju ako je
zanima. wParam i lParam su samo dodatni parametri koji se koriste sa nekim porukama. Vecina poruka
salje dodatne informacije o dogadjaju kroz ova dva parametra.
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
Ovo je sada kritican deo. Ovo je gde se nalazi veci deo inteligencije programa. Kod koji odgovara na poruke
se nalazi u window proceduri. Vas kod mora da proverava da li ga interesuje trenutna poruka. Ako su
poruke vazne onda cete napisati kod koji ce da odradjuje neke operacije, a ako ne onda MORATE pozvati
DefWindowProc i proslediti joj sve parametre koje ste primili kroz window proceduru. Ova DefWindowProc
funkcija reaguje na sve poruke koje mi ne zelimo da koristimo a od vitalnog su znacaja za rad programa ili
sistema.
Jedina poruka na koju vi MORATE reagovati je WM_DESTROY. Ova poruka se salje window proceduri kada
je prozor zatvoren. Dok poruka dodje do vase procedure prozor je vec uklonjen sa ekrana i ona vas samo
obavestava da je sve gotovo o da treba da se pripremite za gasenje programa. Nemate drugog izbora sem
da ugasite program kada jednom dodje do ove tacke. Ako zelite da vam se pruzi sansa da sprecite korisnika
pri zatvaranju vaseg prozora onda morate da odgovorite na WM_CLOSE poruku. Za sada, nazad na
WM_DESTROY: posle rasciscavanja svih resurasa sto je program koristio morate pozvati PostQuitMessage
koja ce poslati WM_QUIT, koji ce redom informisati GetMessage da je program gotov i da treba da vrati
FALSE, sto ce opet okoncati glavnu petlju i ugasiti program. Mozete sami poslati WM_DESTROY poruku
svom prozoru sa DestroyWindow funkcijom.

Lekcija 4: Crtanje Teksta


U ovoj lekciji cemo nauciti kako da "crtamo" tekst preko radnog dela prozora Takodje cemo nauciti o
"device context"-u.
Inzvorni kod mozete skinuti ovde .
Teorija:
Tekst je u Windows-u samo jedan tip GUI objekta. Svaki karakter je sacinjen od brojnih piksela (tackica)
spojenih u jednu celinu prepoznatljivog oblika. Zato ovo nazivamo "crtanje", a ne "pisanje". U normalnim
uslovima ce vas tekst biti na radnoj povrsini vaseg prozora (vi mozete crtati tekst i van vaseg prozora ali je
to jedna sasvim druga prica). Stavljanje teksta na ekran je pod Windows-om drugacije nego pod DOS-om.
U DOS-u je ekran imao dimenzije 80x25 dok u Windows-u ekran koristi vise programa. Zbog ovoga su
uvedena neka pravila koja sprecavaju jedan program da pise po delu ekrana koji se koristi od strane drugog
programa. Windows ovo radi tako sto ogranicava mogucnost crtanja na radnu povrsinu prozora. Ova
velicina nije konstantna jer je korisnik moze menjati bilo kada tako da dinamicki morate odredjivati velicinu
vase radne povrsine.
Pre nego sto pocnete crtanje po vasoj radnoj povrsini morate pitati za dozvolu. Dobro ste procitali, vi
nemate apsolutnu kontrolu nad ekranom kao sto ste to imali u DOS-u vec morate pitati Windows za dozvolu
da crtate po svojoj radnoj povrsini. Windows ce odrediti velicinu vase radne povrsine, font, boje i druge
atribute i poslace vam identifikaciju koja se zove "device context handle" koju mozete koristiti za crtanje.
Pa, sta je "device context"? To je struktura koju koristi Windows. Device context je vezan sa odredjenim
uredjajem, kao na primer sa stampacom ili ekranom. U slucaju ekrana, device context je u vezi i sa
prozorom koji je na njemu prikazan.
Neki od atributa su vezani sa grafikom, kao na primer boje ili font i imaju default vrednosti koje vi mozete
menjati po zelji. One postoje da bi vama olaksale posao time sto necete morati da ih podesavate prilikom
svakog GDI poziva.
Device context je okruzenje cije je atribute podesio Windows i gde neke od njih vi mozete promeniti.
Kada program treba da crta on mora doci do DC(device context) identifikacije. Postoji vise nacina da se
dodje do nje:
pozovite BeginPaint kao odgovor na WM_PAINT poruku
pozovite GetDC kao odgovor na bilo koju drugu poruku
pozovite CreateDC da bi napravili novi DC
Jednu stvar morate upamtiti: posto zavrsite sa DC-om vi ga morate osloboditi pre nego sto pristigne
sledeca poruka. Ovo znaci i da ne smete doci do DC-a u jednoj poruci i osloboditi ga pri nekoj sledecoj.
Windows salje WM_PAINT poruke prozoru da bi ga obavestio da je vreme da se ponovo iscrta. Windows
nigde ne drzi sadrzaj radne povrsine prozora. Umesto toga, kada dodje do trenutka kada je potrebno
ponovo iscrtavanje prozora (kao na primer kada se neki drugi prozor nadje preko naseg pa se zatim ponovo
pomeri negde drugde), Windows posalje WM_PAINT poruku i stavi je na stack poruka. Sam prozor je
odgovoran za iscrtavanje svoje sopstvene radne povrsine. Vi morate sakupiti sve informacije o tome kako
da iscrtate prozor u WM_PAIN sekciji vase window procedure tako da ona moze da iscrta prozor kada joj
pristigne WM_PAINT poruka.
Dalje, postoji i termin "invalid rectangle". Ovo je najmanja povrsina na radnom delu prozora koja mora biti
ponovo iscrtana. Kada Windows detektuje takav region on salje WM_PAINT poruku prozoru. Kao odgovor
na WM_PAINT, prozor dolazi to PAINTSTRUCT strukture koja izmedju ostalog sadrzi i koordinate ovog
pravougla. Ako ne odgovorite na WM_PAINT poruku onda barem morate pozvati DefWindowProc ili
ValidateRect inace ce Windows konstantno slati WM_PAINT.
Evo koraka koje treba preduzeti kao odgovor na WM_PAINT poruku:
1. dodjite to DC identifikacije sa BeginPaint
2. crtajte po radnoj povrsini prozora
3. oslobodite DC sa EndPaint
Ne morate sami zvati ValidateRect. To je automatski ucinjeno pri BeginPaint. Izmedju BeginPaint/EndPaint
mozete zvati bilo koje GDI funkcije da bi crtali po prozoru i skoro sve zahtevaju da im se prosledi DC kroz
neki od parametara.
Sadrzaj:
Napisacemo program koji ce prikazati string "Win32 assembly is great and easy!" u centru prozora.

.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
.DATA
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
OurText db "Win32 assembly is great and easy!",0
.DATA?
hInstance HINSTANCE ?
CommandLine LPSTR ?
.CODE
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
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 hInst
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
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,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE

invoke GetMessage, ADDR msg,NULL,0,0


.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL hdc:HDC
LOCAL ps:PAINTSTRUCT
LOCAL rect:RECT
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_PAINT
invoke BeginPaint,hWnd, ADDR ps
mov hdc,eax
invoke GetClientRect,hWnd, ADDR rect
invoke DrawText, hdc,ADDR OurText,-1, ADDR rect, \
DT_SINGLELINE or DT_CENTER or DT_VCENTER
invoke EndPaint,hWnd, ADDR ps
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax, eax
ret
WndProc endp
end start
Analiza:
Veci deo koda je isti kao u lekciji 3 i samo cu objasniti vaznije izmene.
LOCAL hdc:HDC
LOCAL ps:PAINTSTRUCT
LOCAL rect:RECT
Postoji par lokalnih promenjivih koje koriste GDI funkcije u WM_PAINT odeljku. hdc koristimo da bi sacuvali
identifikaciju DC-a koju nam vraca BeginPaint. ps je PAINTSTRUCT struktura cije nas vrednosti sada ne
interesuju. Ona je data BeginPaint funkciji koja ce je ispuniti sa odgovarajucim vrednostima. Ova struktura
ce se kasnije proslediti EndPaint fukciji kada zavrsimo sa crtanjem. rect je RECT struktura koja izgleda
ovako:
RECT Struct
left LONG ?
top LONG ?
right LONG ?
bottom LONG ?
RECT ends
Lefti i top su koordinate gornjeg levog ugla pravougaonika. Right i bottom su koordinate donjeg desnog
ugla. Napomena: koordinatni pocetak X-Y ose se nalazi u gornjem levom uglu prozora tako da je tacka
y=10 negde ISPOD tacke y=0.
invoke BeginPaint,hWnd, ADDR ps
mov hdc,eax
invoke GetClientRect,hWnd, ADDR rect
invoke DrawText, hdc,ADDR OurText,-1, ADDR rect, \
DT_SINGLELINE or DT_CENTER or DT_VCENTER
invoke EndPaint,hWnd, ADDR ps
Kao odgovor na WM_PAINT poruku vi zovete BeginPaint sa identifikacijom przora po kome zelite da crtate

kao i PAINTSTRUCT strukturu. Posle uspesnog poziva zovete GetClientRect da bi dosli do dimenzija radne
povrsine prozora. Ove dimenzije se vracaju kroz rect promenljivu koju cemo kasnije proslediti DrawText
funkciji kao jedan od parametara. Sintaksa DrawText funkcije je sledeca:
DrawText proto hdc:HDC, lpString:DWORD, nCount:DWORD, lpRect:DWORD, uFormat:DWORD
DrawText API funkcija je jednostavna za koriscenje. Ona se brine o nekim detaljima kao sto je automatsko
formatiranje teksta kako bi on stao u dati prostor, razne konverzije itd, tako da se vi mozete koncentrisati
na string. O TextOut funkciji cemo pricati u sledecoj lekciji. Vec smo rekli da DrawText formatira tekst kako
bi on stao u zadati pravougaonik. Ova funkcija koristi vec izabrani font, boju i pozadinu, a po izlazu u eax
smesta visinu teksta u jedinicama DC-a, u nasem slucaju, pikselima. Parametri su sledeci:
hdc je identifikacija DC-a.
lpString je pointer ka stringu koji zelite da nacrtate u pravougaoniku. Ovaj string mora biti zavrsen sa
NULL karakterom inace vi morate da date koliko je dugacak u nCount parametru.
nCount je broj karaktera u stringu. Ako je string zavrsen sa NULL karakterom nCount mora biti -1,
drugacije mora biti broj karaktera u stringu koji hocete da se nacrta.
lpRect je pointer ka pravougaoniku (struktura tipa RECT) u kome zelite da se crta. Nije moguce crtati
van ovog prevougaonika.
uFormat je vrednost koja govori kako ce se string prikazati u prevougaoniku. Koristimo tri vrednosti
kombinovane sa "or" operatorom:
DT_SINGLELINE - Tekst u jednoj liniji.
DT_CENTER - Horizontalno centriran tekst.
DT_VCENTER - Vertikalno centriran tekst. Mora se koristiti u kombinaciji sa DT_SIGNLELINE.
Posto zavrsite crtanje morate pozvati EndPaint funkciju da bi oslobodili DC.
To je sve. Osnovne tacke u ovoj lekciju su sledece:
Pozivate BeginPaint/EndPaint kao odgovor na WM_PAINT poruku.
Radite sta vec zelite sa radnom povrsinom prozora izmedju BeginPaing i EndPaint poziva.
Ako zelite da crtate po prozoru kao odgovor na neku drugu poruku imate dva izbora:
Koristite GetDc/ReleaseDC funkcije i crtajte izmedju ovih poziva.
Zovite InvalidateRect ili UpdateWindow i obelezite celu povrsinu prozora za crtanje i time
naterajte Windows da posalje WM_PAINT.

Lekcija 5: Jos O Tekstu


Eksperimentisacemo sa jos nekim atributima teksta kao sto su font i boja. Skinite izvorni kod ovde .
Teorija:
Boje u Windows-u su zasnovane na RGB vrednostima (R=reg(crveno), G=green(zeleno), B=blue(plavo)).
Sve boje morate opisati u ovom rezimu. Svaka komponenta boje moze biti broj u vrednosti od 0 do 255
(zauzima 1 bajt). Na primer ako zelite cisto belu onda morete koristiti 255,255,255. Vidite iz primera da je
dobijanje boje koja vam treba prilicno tesko zato sto morate znati kako da mesate boje.
Za boje teksta i pozadine koristimo SetTextColor i SetBkColor. Obe funkcije zahtevaju DC identifikaciju kao i
32 bitnu RGB vrednost. Ova 32 bitna RGB struktura je definisana ovako:
RGB_value struct
unused db 0
blue db ?
green db ?
red db ?
RGB_value ends
Prvi bajt se ne koristi i trebalo bi da bude 0. Redosled ostala tri elementa je plavo, zeleno, crveno. Mi ipak
necemo koristiti ovu strukturu vec cemo napisati makro koji ce nam pomoci. Makro ce primati tri parametra:
crveno(red), zeleno(green) i plavo(blue) i smestice 32 bitnu RGB vrednost u eax. Evo tog makroa:
RGB macro red,green,blue
xor eax,eax
mov ah,blue
shl eax,8
mov ah,green
mov al,red
endm
Ovaj makro mozete staviti u neki include fajl za kasniju upotrebu.
Mozete "napraviti" font koristeci CreateFont ili CreateFontIndirect. Razlika izmedju ove dve funkcije je da
CreateFontIdirect prima samo jedan parametar: pointer ka LOGFONT strukturi. CreateFontIndirect je
fleksibilniji, pogotovo ako vas program treba da cesto menja font. U nasem primeru cemo "stvoriti" samo
jedan font cisto za demonstraciju i to cemo uciniti koristeci CreateFont. Posle CreateFont poziva on ce
vratiti identifikaciju fonta koju moramo da ubacimo u DC. Posle toga ce sve tekst API funkcije koje
pozovemo da koriste izabrani font za taj DC.
Sadrzaj:
.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\gdi32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\gdi32.lib
RGB macro red,green,blue
xor eax,eax
mov ah,blue
shl eax,8
mov ah,green
mov al,red
endm
.data

ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
TestString db "Win32 assembly is great and easy!",0
FontName db "script",0
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
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 hInst
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
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,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL hdc:HDC
LOCAL ps:PAINTSTRUCT

LOCAL hfont:HFONT
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_PAINT
invoke BeginPaint,hWnd, ADDR ps
mov hdc,eax
invoke CreateFont,24,16,0,0,400,0,0,0,OEM_CHARSET,\
OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,\
DEFAULT_QUALITY,DEFAULT_PITCH or FF_SCRIPT,\
ADDR FontName
invoke SelectObject, hdc, eax
mov hfont,eax
RGB 200,200,50
invoke SetTextColor,hdc,eax
RGB 0,0,255
invoke SetBkColor,hdc,eax
invoke TextOut,hdc,0,0,ADDR TestString,SIZEOF TestString
invoke SelectObject,hdc, hfont
invoke EndPaint,hWnd, ADDR ps
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
Analiza:
invoke CreateFont,24,16,0,0,400,0,0,0,OEM_CHARSET,\
OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,\
DEFAULT_QUALITY,DEFAULT_PITCH or FF_SCRIPT,\
ADDR FontName
CreateFont stvara logicki font koji je najpriblizniji onom sto smo mi trazili kroz date parametre. Ova funkcija
ima vise parametara od bilo koje druge funkcije u Windows-u i vraca identifikaciju fonta koju cemo koristiti
sa SelectObject funkcijom. Ove parametre cemo ispitati do detalja.
CreateFont proto nHeight:DWORD,\
nWidth:DWORD,\
nEscapement:DWORD,\
nOrientation:DWORD,\
nWeight:DWORD,\
cItalic:DWORD,\
cUnderline:DWORD,\
cStrikeOut:DWORD,\
cCharSet:DWORD,\
cOutputPrecision:DWORD,\
cClipPrecision:DWORD,\
cQuality:DWORD,\
cPitchAndFamily:DWORD,\
lpFacename:DWORD
1. nHeight je zeljena visina karaktera. NULL za predefinisanu visinu.
2. nWidth je zeljena sirina karaktera. Ovo bi trebalo da bude NULL kako bi Windows odredio sirinu u
zavisnosti od visine. Kako u primeru predefinisana vrednost cini slova ne citkim ja sam stavio 16
umesto NULL.
3. nEscapement je orijentacija jednog karaktera u odnosu na drugi u desetinama stepena. Ovo bi trebalo
da bude 0. Ako stavite 900 dobicete sve karaktere jedan iznad drugog. Ako stavite 1800 oni ce ici u
nazad, a ako stavite 2700 karakteri ce biti jedan ispod drugog.
file:///D|/Download%20Free%20zona/Knjige%20Racunari%20staro/Win32asm/tut005.html[18.4.2010 3:08:07]

4. nOrientation je nagib karaktera u desetinama stepena. Sa 900 ce svi karakteri biti polozeni. Koristite
1800 kada su karakteri jedan ispod drugog...
5. nWeight je gustina karaktera. Windows je predefinisao sledece velicine:
FW_DONTCARE equ 0
FW_THIN equ 100
FW_EXTRALIGHT equ 200
FW_ULTRALIGHT equ 200
FW_LIGHT equ 300
FW_NORMAL equ 400
FW_REGULAR equ 400
FW_MEDIUM equ 500
FW_SEMIBOLD equ 600
FW_DEMIBOLD equ 600
FW_BOLD equ 700
FW_EXTRABOLD equ 800
FW_ULTRABOLD equ 800
FW_HEAVY equ 900
FW_BLACK equ 900
6. cItalic - 0 za normalne, bilo sta drugo za kose karaktere.
7. cUnderline - 0 za normalne, bilo sta druo za podvucene karaktere.
8. cStrikeOut - 0 za normalne, bilo sta drugo za precrtane karaktere.
9. cCharSet je set karaktera za font. Ovo bi trebalo da bude OEM_CHARSET koji ce prepustiti Windows-u
da izabere font koji zavisi od verzije Windows-a.
10. cOutputPrecision je vrednost koja govori koliko blizu mora da bude izabrani font sa onim sto smo mi
trazili. Uglavnom cete koristiti OUT_DEFAULT_PRECIS.
11. cClipPrecision je preciznost klipovanja. Ovo odredjuje nacin klipovanja karaktera koji su delimicno van
pravougaonika odredjenog za crtanje. Predefinisana vrednost je CLIP_DEFAULT_PRECIS.
12. cQuality je kvalitet rezultujuceg crtanja. Ovaj parametar odredjuje koliko ce pazljivo GDI pokusati da
prilagodi zeljeni logicki font fizickom fontu. Postoje tri vrednosti: DEFAULT_QUALITY, PROOF_QUALITY
i DRAFT_QUALITY.
13. cPitchAndFamily je tip fonta. Moguce vrednosti ovog parametra morate kombinovati sa "or"
operatorom.
14. lpFacename je pointer ka ASCIIZ stringu sto odredjuje naziv fizickog fonta koji ce se koristiti.
Ovaj opis nije nikako sveobuhvatan. Obratite se svojoj Win32 API dokumentaciji za vise detalja.
invoke SelectObject, hdc, eax
mov hfont,eax
Posto dobijemo identifikaciju logickog fonta, moramo je koristiti da bi izabrali font u DC koristeci
SelectObject. SelectObject stavlja nove GDI objekte kao sto su pera, cetke i fontovi u DC i njima ce crtati
sve GDI funkcije. Povratna vrednost je identifikacija starog objekta iz tog DC-a koju bi trebalo da sacuvamo
da bi je kasnije vratili u DC.
RGB 200,200,50
invoke SetTextColor,hdc,eax
RGB 0,0,255
invoke SetBkColor,hdc,eax
Koristite RGB makro da dodjete do 32 bitne RGB vrednosti koju koristimo sa SetColorText i SetBkColor.
invoke TextOut,hdc,0,0,ADDR TestString,SIZEOF TestString
Zovite TextOut funkciju za nacrtate tekst na radnoj povrsini prozora. Teksi i njegova boja ce odgovarati
onome sto smo prethodno podesili.
invoke SelectObject,hdc, hfont
Kada zavrsimo sa fontom trebalo bi da vratimo stari font u DC. Tako treba postupiti sa svim objektima koje
stavljamo u DC.

Lekcija 6: Unos Sa Tastature


Sada cemo nauciti kako jedan Windows program prima informacije preko tastature. Skinite primer ovde .
Teorija:
Posto postoji samo jedna tastatura na PC-u svi programi moraju da je dele. Windows je odgovoran za
slanje informacija o unosu sa tastature onom prozoru koji je trenutno aktivan ili drugacije receno, prozoru
koji trenutno ima fokus.
I ako moze biti vise prozora na ekranu, samo jedan od njih ima fokus i samo ce taj primati informacije o
promeni stanja tastera. Mozete razlikovati aktivan od ne aktivnog prozora po izgledu njihovog title bara.
Postoje dva tipa poruka od tastature, u zavisnosti od nacina na koji zelite da gledate na tastaturu. Na nju
mozete gledati kao na skup tastera. U ovom slucaju ako pritisnete neki taster Windows ce poslati
WM_KEYDOWN poruku aktivnom prozoru. Na tastaturu mozete gledati kao na uredjaj za unos teksta tako
da ce Windows slati WM_CHAR poruku i karater uz nju. U stvari Windows i dalje salje WM_KEYDOWN i
WM_KEYUP poruke prozoru ali ce one biti prevedene sa TranslateMessage funkcijom. Window procedura
moze da koristi sve tri poruke ili bilo koju njihovu kombinaciju u zavisnosti od potrebe. U ovoj lekciji cemo
se koncentrisati na upotrebu WM_CHAR poruke.
Primer:
.386
.model flat,stdcall
option casemap:none WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\gdi32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\gdi32.lib
.data
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
char WPARAM 20h ; the character the program receives from keyboard
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
.code
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
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 hInst
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
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,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL hdc:HDC
LOCAL ps:PAINTSTRUCT
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_CHAR
push wParam
pop char
invoke InvalidateRect, hWnd,NULL,TRUE
.ELSEIF uMsg==WM_PAINT
invoke BeginPaint,hWnd, ADDR ps
mov hdc,eax
invoke TextOut,hdc,0,0,ADDR char,1
invoke EndPaint,hWnd, ADDR ps
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
Analiza:
char WPARAM 20h ; the character the program receives from keyboard
Ovo je promenljiva u koju cemo smestiti karakter primljen od tastature. Kako je karakter poslat u WPARAM
parametru Window procedure definisacemo promenljivu tipa WPARAM zbog jednostavnosti. Pocetna
vrednost je 20h tj. razmak jer kada se prozor pojavi po prvi put nema nikakvog unosa pa zelimo da
prikazemo samo "razmak"
.ELSEIF uMsg==WM_CHAR
push wParam
pop char
invoke InvalidateRect, hWnd,NULL,TRUE
Ovo je dodato window proceduri da bi obradjivala WM_CHAR poruku. Kod samo stavlja karakter u
promenljivu zvanu "char" i onda poziva InvalidateRect. InvalidateRect obelezava dati pravougaonik za
ponovo crtanje i tako primorava Windows da posalje WM_PAINT poruku window proceduri. Sintaksa izleda
ovako:
InvalidateRect proto hWnd:HWND,\

lpRect:DWORD,\
bErase:DWORD
lpRect je pointer ka pravougaoniku na radnoj povrsini prozora koji zelimo da obelezimo za ponovno crtanje.
Ako je ovo NULL ceo prozor ce biti obelezen.
bErase govori Windowsu da treba da obrise pozadinu. Ako je ovo TRUE onda ce windows obrisati pozadinu
u pravougaoniku cim se pozove BeginPaint.
Znaci logika je ova: cuvamo sve informacije koje cemo koristiti za crtanje prozora i pozivamo WM_PAINT
poruku da bi nacrtali ono sto zelimo. Podrazumeva se da kod koji se nalazi pod WM_PAINT mora biti
spreman da obradi informacije koje su za njega pripremljene.
Mozemo da crtamo po prozoru i odmah po primljenoj WM_CHAR poruci koristeci GetDC/ReleaseDC par, sve
je u redu i tako ali ce biti zabavno kada ce prozor morati da se ponovo iscrta. Kako je kod za crtanje u
WM_CHAR poruci, window procedura nece biti u stanju da nacrta i nas karakter. Zakljucak je da je bolje
sav kod u vezi sa crtanjem ostaviti u WM_PAINT sekciji.
invoke TextOut,hdc,0,0,ADDR char,1
Kada je InvalidateRect pozvana, ona salje WM_PAINT poruku nasoj window proceduri tako da je kod iz
WM_PAINT sekcije izvrsen. On ce pozvati BeginPaint, doci do DC-a, zvati TextOut koji crta karakter na
radnoj povrsini, na koordinatama x=0, y=0 i eventualno pozvati EndPaint. Cak i kada prozor spustimo pa
ponovo vratimo na dekstop sve ce ostati ispravno jer ce se generisati WM_PAINT poruka koja ce ponovo
nacrtati poslednji uneti karakter.

Lekcija 7: Unos Preko Misa


Naucicemo kako da dodjemo i kako da odgovorimo na unos preko misa. Program iz primera ce cekati da se
klikne levim dugmetom misa negde na radnoj povrsini prozora, a zatim ce prikazati string na bas tom
kliknutom mestu. Skinite primer ovde .
Teorija:
Kao i u slucaju tastature, Windows i ovde detektuje i obavestava nas o stanju misa. Ovo obuhvata levi i
desni klik, pomeranje kursora preko prozora, duple klikove. Nasuprot tastaturi, koja je preusmerena ka
prozoru koji ima fokus, poruke misa se salju prozoru kji se trenutno nalazi ispod kursora, bio on aktivan ili
ne. Takodje postoje i poruke kada je mis van radne povrsine ali ih uglavnom mozemo ingorisati i samo se
koncentrisati na radnu povrsinu.
Postoje dve poruke za svako dugme na misu: WM_LBUTTONDOWN, WM_RBUTTONDOWN i
WM_LBUTTONUP, WM_RBUTTONUP poruke. Za miseve sa 3 dugmeta postoji takodje i W_MBUTTONDOWN
i WM_MBUTTONUP. Kada se kursor pomera iznad radne povrsine prozora Windows mu salje
WM_MOUSEMOVE poruku.
Prozor moze da prima poruke o duplim klikovima: WM_LBUTTONDBCLK i WM_RBUTTONDBCLK, ako i samo
ako klasa tog prozora ima CS_DBLCLKS stil. Drugacije, prozor ce samo dobijati serije *BUTTONDOWN i
*BUTTONUP poruka.
Za sve te poruke vrednost lParam parametra sadrzi koorditane misa. low word je x koordinata dok je high
word y koordinata relativna gornjem levom uglu radne povrsine datog prozora. wParam je stanje dugmica
misa i Shift i Ctrl dugmica.
Primer:
.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\gdi32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\gdi32.lib
.data
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
MouseClick db 0 ; 0=no click yet
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hitpoint POINT <>
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
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 hInst
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
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,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL hdc:HDC
LOCAL ps:PAINTSTRUCT
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_LBUTTONDOWN
mov eax,lParam
and eax,0FFFFh
mov hitpoint.x,eax
mov eax,lParam
shr eax,16
mov hitpoint.y,eax
mov MouseClick,TRUE
invoke InvalidateRect,hWnd,NULL,TRUE
.ELSEIF uMsg==WM_PAINT
invoke BeginPaint,hWnd, ADDR ps
mov hdc,eax
.IF MouseClick
invoke lstrlen,ADDR AppName
invoke TextOut,hdc,hitpoint.x,hitpoint.y,ADDR AppName,eax
.ENDIF
invoke EndPaint,hWnd, ADDR ps
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start

Analiza:
.ELSEIF uMsg==WM_LBUTTONDOWN
mov eax,lParam
and eax,0FFFFh
mov hitpoint.x,eax
mov eax,lParam
shr eax,16
mov hitpoint.y,eax
mov MouseClick,TRUE
invoke InvalidateRect,hWnd,NULL,TRUE
Window procedura ceka levi klik. Kada primi WM_LBUTTONDOWN lParam sadrzi koordinate kursora na
radnoj povrsini i te koordinate zatim cuva u promenljivoj tipa POINT koja je definisana ovako:
POINT STRUCT
x dd ?
y dd ?
POINT ENDS
Onda podesava MouseClick flag na TRUE da bi obelezila da postoji barem jedan levi klik na radnu povrsinu.
mov eax,lParam
and eax,0FFFFh
mov hitpoint.x,eax
Kako je x koordinata low word lParam parametra, a clanovi POINT strukture su 32 bita veliki mi moramo da
nulisemo high word eax-a pre no sto ga sacuvamo u hitpoint.x .
shr eax,16
mov hitpoint.y,eax
Posto je y koordinata hight word lParam parametra, moramo da je prvo stavimo u low word eax-a (ax) i da
nulisemo hight word istog pre nego sto ga sacuvamo u hitpoint.y . Ovo radimo pomeranjem bitova iz eax-a
za 16 mesta u desno.
Posto sacuvamo koordinate misa podesimo MouseClick na TRUE kako bi kod iz WM_PAINT odeljka znao da
treba da nacrta string. Onda zovemo InvalidateRect kako bi primorali Windows da nam posalje WM_PAINT
poruku.
.IF MouseClick
invoke lstrlen,ADDR AppName
invoke TextOut,hdc,hitpoint.x,hitpoint.y,ADDR AppName,eax
.ENDIF
Kod iz WM_PAINT sekcije mora da proveri da li je MouseClick TRUE. Na pocetku programa MouseClick mora
biti FALSE jer se do tada nije desio ni jedan klik i ne treba crtati nista na prozoru pre nego dodje do jednog.
Ako je doslo do klika onda se crta string na radnoj povrsini prozora pocevsi od koordinata misa. Pozivamo
lstrlen da bi dosli do duzine stringa koju cemo dati kao poslednji parametar TextOut funkcije.

Lekcija 8: Meni
U ovoj lekciji cemo nauciti kako da stavimo meni na nas prozor. Skinite primer 1 i primer 2 .
Teorija:
Meni je jedan od najvaznijih delova naseg prozora. On pretstavlja listu akcija koje nas program nudi
korisniku. Korisnik ne treba da cita prirucnik programa kako bi mogao da koristi program vec bi trebalo da
mu bude dovoljno da proceslja meni pa da zna sta program moze da uradi i da odmah krene da se igra sa
programom. Kako je meni alatka koja omogucuje korisniku brzi start sa radom on mora da postuje
odredjene standarde. Na primer prva dva pod menija bi trebalo da budu 'File' i 'Edit' a zadnji 'Help'. Sve
vase menije mozete dodati izmedju 'Edit' i 'Help' pod menija. Ako element menija pokrece dijalog onda
njegovo ime treba da se zavrsava sa tri tacke (...).
Meni je tip resursa. Postoji nekoliko tipova resurasa kao na primer dijalog, ikone, bitmap slike, itd. Resursi
su opisani u drugom fajlu koji uglavnom nosi ekstenziju .rc . Zatim se resursi kombinuju sa ostatkom
izvornog koda pri linkovanju pa je rezultat EXE sa kodom i resursima.
Resurs skriptove mozete pisati koristeci bilo koji tekst editor. Oni su sacinjeni od fraza koje opisuju izgled i
ostale osobine svakog pojedinacnog resursa. Ipak pisanje ovih skriptova je krajnje nepotrebno jer postoje
vizuelni editori kojima puno lakse pravimo resurs skripte. Ovakvi se editori uglavnom nalaze u paketima tipa
Visual C++, Borland C++ itd ali mozete izabrati i neki od besplatnih resors editora sto se mogu naci na
internetu.
Ovako se opisuje jedan meni u resursu:
MyMenu MENU
{
[menu list here]
}
C programeri ce videti da je ovo slicno definisanju strukture. MyMenu je ime menija praceno sa MENU
komandom i listom u { } zagradama. Pored ovih zagrada mozete koristiti i BEGIN / END par kao u Pascalu
ako vam se tako vise svidja.
Lista u meniju moze biti ili MENUITEM ili POPUP komanda.
MENUITEM komanda definise jednostavan element menija koji ne poziva nikakav pod meni. Sintaksa je
sledeca:
MENUITEM "&text", ID [,options]
Pocinje sa MENUITEM komandom pracenom tekstom koji zelimo da koristimo za taj meni. Obratite paznju
na & simbol. On cini karakter koji ga prati da bude podvucen. Posle teksta dolazi ID menija. ID je broj koji
ce biti koriscen za identifikaciju menija kada se primi window poruka u window proceduri i zato svaki ID
mora biti jedinstven.
Opcije su izborne. Set opcija je sledeci:
GRAYED - meni nije aktivan i ne pokrece WM_COMMAND poruku. Tekst je sive boje.
INACTIVE - meni nije aktivan i ne pokrece WM_COMMAND poruku. Tekst je normalno prikazan.
MENUBREAK - ovaj meni i svaki sledeci ce se pojaviti u novoj liniji.
HELP Ovaj i svaki sledeci meni je poravnan na desno.
Mozete koristiti jednu od gore navedenih opcija ili ih mozete kombinovati koristeci "or" operator. Paznja:
INACTIVE i GRAYED se ne mogu kombinovati.
POPUP komanda ima sledecu sintaksu:
POPUP "&text" [,options]
{
[menu list]
}
POPUP komanda definise element koji kada klinkemo otvara pod meni. Lista moze biti sacinjena od
MENUITEM ili POPUP elemenata. Pored ovi postoji i jos jedan tip: MENUITEM SEPARATOR koji prikazuje
samo jednu horizontalnu liniju koju mozemo koristiti za grupisanje pod menija.
Posto zavrsite sa skriptom treba da pripisete meni nekom prozoru.
Ovo mozete uraditi na dva nacina.
U lpszMenuName clanu WNDCLASSEX strkuturi. Ako imate meni nazvan "FirstMenu" mozete ga
pripisati prozoru ovako:

file:///D|/Download%20Free%20zona/Knjige%20Racunari%20staro/Win32asm/tut008.html[18.4.2010 3:08:06]

.DATA
MenuName db "FirstMenu",0
...........................
...........................
.CODE
...........................
mov wc.lpszMenuName, OFFSET MenuName
...........................
Kroz Menu Handle parametar CreateWindowEx funkcije:
.DATA
MenuName db "FirstMenu",0
hMenu HMENU ?
...........................
...........................
.CODE
...........................
invoke LoadMenu, hInst, OFFSET MenuName
mov hMenu, eax
invoke CreateWindowEx,NULL,OFFSET ClsName,\
OFFSET Caption, WS_OVERLAPPEDWINDOW,\
CW_USEDEFAULT,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,\
NULL,\
hMenu,\
hInst,\
NULL\
...........................
Koja je razlika izmedju ova dva nacina?
Kada pripisete meni kroz WNDCLASSEX strukturu meni postaje default za sve prozore te klase.
Ako zelite da svaki stvoreni prozor iste klase ima razliciti meni onda morate izabrati drugi nacin. U tom
slucaju ce svaki prozor koji stvorimo sa CreateWindowEx i navedemo menu handle imati taj nas meni
umesto menija definisanog u WNDCLASSEX strukturi.
Sada cemo videti kako meni obavestava prozor u akcijama koje korisnik pokrece.
Kada korisnik izabere neki element menija, window procedura ce primiti WM_COMMAND poruku. Low word
wParam parametra sadrzi ID elementa.
Sada imamo sve potrebne informacije da stvorimo i koristimo menije. Ajmo da to i uradimo.
Primer:
Ovaj prvi primer pokazuje kako napraviti i koristiti meni ubacen kroz klasu prozora.
.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
.data
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
MenuName db "FirstMenu",0 ; The name of our menu in the resource file.
Test_string db "You selected Test menu item",0
Hello_string db "Hello, my friend",0
Goodbye_string db "See you again, bye",0
.data?
hInstance HINSTANCE ?

CommandLine LPSTR ?
.const
IDM_TEST equ 1 ; Menu IDs
IDM_HELLO equ 2
IDM_GOODBYE equ 3
IDM_EXIT equ 4
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
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 hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,OFFSET MenuName ; Put our menu name here
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
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,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF ax==IDM_TEST
invoke MessageBox,NULL,ADDR Test_string,OFFSET AppName,MB_OK
.ELSEIF ax==IDM_HELLO

invoke MessageBox, NULL,ADDR Hello_string, OFFSET AppName,MB_OK


.ELSEIF ax==IDM_GOODBYE
invoke MessageBox,NULL,ADDR Goodbye_string, OFFSET AppName, MB_OK
.ELSE
invoke DestroyWindow,hWnd
.ENDIF
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
Menu.rc
#define IDM_TEST 1
#define IDM_HELLO 2
#define IDM_GOODBYE 3
#define IDM_EXIT 4
FirstMenu MENU
{
POPUP "&PopUp"
{
MENUITEM "&Say Hello",IDM_HELLO
MENUITEM "Say &GoodBye", IDM_GOODBYE
MENUITEM SEPARATOR
MENUITEM "E&xit",IDM_EXIT
}
MENUITEM "&Test", IDM_TEST
}
Analiza:
Ajmo da prvo analiziramo resurs.
#define IDM_TEST 1 /* equal to IDM_TEST equ 1*/
#define IDM_HELLO 2
#define IDM_GOODBYE 3
#define IDM_EXIT 4
Ove linije su definisale meni ID koriscene u skripti. Ovo mogu biti bilo koje vrednosti sve dok su jedinstvene
u meniju.
FirstMenu MENU
Deklarisite meni sa MENU komandom.
POPUP "&PopUp"
{
MENUITEM "&Say Hello",IDM_HELLO
MENUITEM "Say &GoodBye", IDM_GOODBYE
MENUITEM SEPARATOR
MENUITEM "E&xit",IDM_EXIT
}
Definisite 4 meni elementa gde je treci horizontalna linija.
MENUITEM "&Test", IDM_TEST
Definisite "menu bar" u glavnom meniju.
Sada cemo ispitati izvorni kod.
MenuName db "FirstMenu",0 ; The name of our menu in the resource file.
Test_string db "You selected Test menu item",0
Hello_string db "Hello, my friend",0
Goodbye_string db "See you again, bye",0
MenuName je ime menija u resursu. Mozete definisati vise od jednog menija tako da je ovo neophodno da
bi se meniji razlikovali. Sledece tri linije definisu tekst koji ce biti prikazan u MessageBox prozoru koji je

prikazan kada korisnih klikne na neki od elemenata menija.


IDM_TEST equ 1 ; Menu IDs
IDM_HELLO equ 2
IDM_GOODBYE equ 3
IDM_EXIT equ 4
Definisite meni ID za koriscenje u window proceduri. Ove vrednosti moraju biti identicne vrednostima u
resursu.
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF ax==IDM_TEST
invoke MessageBox,NULL,ADDR Test_string,OFFSET AppName,MB_OK
.ELSEIF ax==IDM_HELLO
invoke MessageBox, NULL,ADDR Hello_string, OFFSET AppName,MB_OK
.ELSEIF ax==IDM_GOODBYE
invoke MessageBox,NULL,ADDR Goodbye_string, OFFSET AppName, MB_OK
.ELSE
invoke DestroyWindow,hWnd
.ENDIF
U window proceduri se bavimo sa WM_COMMAND porukama. Kada korisnik izabere neki element, meni ID
istog je prosledjen kroz WM_COMMAND u low word wParam parametra. Ovo znaci da kada primimo poruku
i stavimo wParam u eax treba da uporedimo ax sa meni ID. U prva tri slucaja, kada korisnik izabere Test,
Say Hello, ili Say GoodBye, MessageBox ce se prikazati.
Ako korisnik izabere Exit onda pozivamo DestroyWindow sa identifikacijom naseg prozora.
Kao sto vidite deklarisanje imena menija u klasi prozora je veoma jednostavno. Takodje mozete da koristite
i alternativni metod. Ovde necu prikazati ceo izvorni kod. Resurs je isti u oba slucaja i postoje samo sitne
razlike u izvornom kodu.
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hMenu HMENU ? ; handle of our menu
Definisemo promenljivu tipa HMENU (sinonim za DWORD) u kojoj cemo drzati menu handle.
invoke LoadMenu, hInst, OFFSET MenuName
mov hMenu,eax
INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,hMenu,\
hInst,NULL
Pre pozivanja CreateWindowEx pozivamo LoadMenu sa identifikacijom programa (instance handle) i
pointerom ka imenu menija. LoadMenu vraca handle naseg menija iz resursa koji zastim prosledjujemo
CreateWindowEx funkciji.

Lekcija 9: Kontrole
U ovoj lekciji cemo se upoznati sa kontrolama koje su bitan element ulaza i izlaza informacija iz naseg
programa. Skinite primer ovde .
Teorija:
Windows nam pruza nekoliko predefinisanih klasa prozora koje mi mozemo koristiti u nasim programima.
Ugavnom ih koristimo kao komponente dijaloga tako da su te kontrole deca nasih dijaloga. Ove kontrole
same odgovaraju na svoje poruke, a zatim obavestavaju roditelja o promenama koje su se dogodile. One
su ogromna olaksica programerima i treba ih koristiti sto vise. U ovoj lekciji cu ih sve smestiti na obican
prozor da bi vam pokazao kako se mogu stvoriti i koristiti. Ove kontrole je bolje koristiti na dijalozima.
Primeri predefinisanih klasa prozora su dugme (BUTTON), liste (LISTBOX), polja za unos teksta (EDIT) i
druge.
Da bi stvorili kontrolu pozivamo CreateWindow ili CreateWindowEx. Nije potrebno registrovanje klase
prozora jer je ona vec registrovana. Parametar koji sadrzi ime klase MORA biti predefinisana klasa kontrole
koju zelimo da stvorimo. Ako zelite da stvorite dugme morate staviti "BUTTON" za ime klase. Drugi
parametri koje morate staviti su handle roditelja i ID kontrole. ID kontrole mora biti jedinstven jer se koristi
za razlikovanje jedne kontrole od drugih.
Posto je kontrola stvorena ona ce poslati poruku roditelju cim joj se stanje promeni. Uglavnom decu prozora
stvarate za vreme WM_CREATE poruke roditelja. Deca salju WM_COMMAND poruku prozoru sa svojim IDom u low word delu wParam parametra dok je kod poruke u hight word delu istog. Handle kontrole se
nalazi u lParam parametru. Svaka klasa ovih kontrola ima svoj skup kodova poruka. Detalje mozete naci u
Win32 API dokumentaciji.
Roditelj moze takodje slati komande svojoj deci koristeci SendMessage funkciju. SendMessage salje datu
poruku sa wParam i lParam parametrima poruke prozoru koji je odredjen sa handle parametrom
SendMessage funkcije. Ovo je veoma korisna funkcija jer moze slati poruke bilo kom prozoru ciji handle
imate.
Posto napravimo kontrole, roditelj mora paziti na WM_COMMAND poruke da bi bio u stanju da prati sta se
desava sa kontrolama.
Primer:
Napravicemo prozor koji sadrzi polje za unos teksta i dugme. Kada stisnete dugme pojavljuje se
MessageBox sa tekstom koji ste uneli u polje za tekst. Takodje postoji i meni sa 4 elementa:
1.
2.
3.
4.

Say Hello Stavlja ovaj tekst u polje sa tekstom


Clear Edit Box Brise sadrzaj polja sa tekstom
Get Text Prikazuje MessageBox sa tekstom iz polja
Exit Izlazi iz programa.

.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
.data
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
MenuName db "FirstMenu",0
ButtonClassName db "button",0
ButtonText db "My First Button",0
EditClassName db "edit",0
TestString db "Wow! I'm in an edit box now",0
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?

hwndButton HWND ?
hwndEdit HWND ?
buffer db 512 dup(?) ; buffer to store the text retrieved from the edit box
.const
ButtonID equ 1 ; The control ID of the button control
EditID equ 2 ; The control ID of the edit control
IDM_HELLO equ 1
IDM_CLEAR equ 2
IDM_GETTEXT equ 3
IDM_EXIT equ 4
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
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 hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_BTNFACE+1
mov wc.lpszMenuName,OFFSET MenuName
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
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName, \
ADDR AppName, WS_OVERLAPPEDWINDOW,\
CW_USEDEFAULT, CW_USEDEFAULT,\
300,200,NULL,NULL, hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL

.ELSEIF uMsg==WM_CREATE
invoke CreateWindowEx,WS_EX_CLIENTEDGE, ADDR EditClassName,NULL,\
WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT or\
ES_AUTOHSCROLL,\
50,35,200,25,hWnd,8,hInstance,NULL
mov hwndEdit,eax
invoke SetFocus, hwndEdit
invoke CreateWindowEx,NULL, ADDR ButtonClassName,ADDR ButtonText,\
WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON,\
75,70,140,25,hWnd,ButtonID,hInstance,NULL
mov hwndButton,eax
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF lParam==0
.IF ax==IDM_HELLO
invoke SetWindowText,hwndEdit,ADDR TestString
.ELSEIF ax==IDM_CLEAR
invoke SetWindowText,hwndEdit,NULL
.ELSEIF ax==IDM_GETTEXT
invoke GetWindowText,hwndEdit,ADDR buffer,512
invoke MessageBox,NULL,ADDR buffer,ADDR AppName,MB_OK
.ELSE
invoke DestroyWindow,hWnd
.ENDIF
.ELSE
.IF ax==ButtonID
shr eax,16
.IF ax==BN_CLICKED
invoke SendMessage,hWnd,WM_COMMAND,IDM_GETTEXT,0
.ENDIF
.ENDIF
.ENDIF
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
Analiza:
Analizirajmo ovaj program.
.ELSEIF uMsg==WM_CREATE
invoke CreateWindowEx,WS_EX_CLIENTEDGE, \
ADDR EditClassName,NULL,\
WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT\
or ES_AUTOHSCROLL,\
50,35,200,25,hWnd,EditID,hInstance,NULL
mov hwndEdit,eax
invoke SetFocus, hwndEdit
invoke CreateWindowEx,NULL, ADDR ButtonClassName,\
ADDR ButtonText,\
WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON,\
75,70,140,25,hWnd,ButtonID,hInstance,NULL
mov hwndButton,eax
Stvaramo kontrole za vreme WM_CREATE poruke naseg glavnog prozora. Pozivamo CreateWindowEx sa
ekstra stilom podesenim na WS_EX_CLIENTEDGE koji cini da ivice prozora izgledaju uronjene u roditelja.

Ime svake klase je predefinian i to je "EDIT" za polje sa tekstom i "BUTTON" za dugme. Zatim ubacimo stil
kontrola. Svaki tip kontrole ima svoj skup ovih stilova koji je sacinjen od nekoliko standardnih i nekoliko
ekstra stilova. Na primer dodatak standardnom setu stilova za "BUTTON" klasu su svi stilovi koji pocinju sa
"BS_" sto je skracenica za "button style". Stilovi za polje sa tekstom pocinju sa "ES_"" sto je skacenica za
"edit style". Ove stilove morate da potrazite u Win32 API dokumentaciji. ID kontrole stavljamo na mesto
identifikacije menija. Ovo je u redu jer kontrole nemaju nikakve menije.
Nakon stvaranja svake kontrole cuvamo handle svake od njih u promenljive radi kasnije upotrebe.
SetFocus zovemo da bi dali fokus polju za unos teksta tako da korisnik moze odmah krenuti sa kucanjem
teksta.
Sada dolazi uzbudljivi deo. Svaka kontrola salje obavestenja roditelju kroz WM_COMMAND.
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF lParam==0
Prisetite se da elementi menija takodje salju WM_COMMAND poruku da bi obavestili prozor o svom stanju.
Ako se pitate kako mozete razlikovati WM_COMMAND poruku prozora od WM_COMMAND poruke kontrole,
malo nize cete naici na odgovor.
type
Low word of wParam High word of wParam lParam
Menu Menu ID
0
0
Control Control ID
Notification code
Child Window Handle
Vidite da treba proveriti lParam parametar. Ako je nula onda je WM_COMMAND stigao od menija. Ne
mozete koristiti wParam za razlikovanje menija i kontrola zato sto identifikacija menija i identifikacija
kontrola mogu biti identicne, a kod poruke moze biti nula.
.IF ax==IDM_HELLO
invoke SetWindowText,hwndEdit,ADDR TestString
.ELSEIF ax==IDM_CLEAR
invoke SetWindowText,hwndEdit,NULL
.ELSEIF ax==IDM_GETTEXT
invoke GetWindowText,hwndEdit,ADDR buffer,512
invoke MessageBox,NULL,ADDR buffer,ADDR AppName,MB_OK
Mozete staviti tekst u polje za unos teksta koristeci SetWindowText, a mozete izbrisati sav tekst pozivom na
SetWindowText sa NULL parametrom. SetWindowText je funkcija opste primene. Mozete je koristiti da
menjate naziv prozora ili tekst ispisan na dugmetu.
Da bi dobili tekst iz polja koristite GetWindowText.
.IF ax==ButtonID
shr eax,16
.IF ax==BN_CLICKED
invoke SendMessage,hWnd,WM_COMMAND,IDM_GETTEXT,0
.ENDIF
.ENDIF
Gore prikazan kod se bavi akcijama koje treba izvrsiti kada korisnik stisne dugme. Prvo proverava low word
wParam parametra da vidi da li je ID, ID dugmeta. Ako jeste onda proverava hight word wParam parametra
da vidi da li je kod BN_CLICKED taj koji je poslat kada je dugme pritisnuto.
Interesantan deo dolazi kada smo sigurni da je kod poruke BN_CLICKED. Zelimo da dodjemo do teksta
polja za unos i prikazemo ga u MessageBox-u. Mozemo napraviti duplikat koda u IDM_GETTEXT sekciji
iznad ali to nema smisla. Ako nekako mozemo poslati WM_COMMAND poruku sa low word-om wParam
parametra koji sadrzi IDM_GETTEXT onda mozemo izbeci dupliranje koda i tako pojednostaviti nas program.
SendMessage je odgovor na ovu nasu potrebu. Ova funkcija salje bilo koju poruku sa bilo kojim
vrednostima parametara. Tako da umesto dupliranja koda saljemo WM_COMMAND poruku nasem prozoru
sa IDM_GETTEXT i 0. Ovo ce proizvesti isti rezultat kao i klik na "Get Text" element menija. Nasa window
procedura nece primetiti nikakvu razliku.
Koristite ovu tehniku sto cesce jer ce kod tako biti organizovaniji.
I na kraju, ne zaboravite TranslateMessage funkciju u glavnoj petlji. Kako korisnik mora ubaciti tekst u polje
sa tekstom, program mora prevesti unos sa tastature u citak tekst. Ako zaboravite ovu funkciju onda
korisnik nece nista moci da ukuca u polje sa tekstom.

Lekcija 10: Dijalog Kao Glavni Prozor


Sada dolazimo do stvarno zanimljivog dela GUI-a: dijaloga. U ovoj i sledecoj lekciji cemo uciti kako koristiti
dijalog kao glavni prozor aplikacije. Skinite prvi primer ovde , a drugi primer ovde .
Teorija:
Ako ste se igrali malo vise sa primerima iz proslih lekicja primetili ste da ne mozete da menjate fokus
kontrola koristeci TAB. Jedini nacin da ovo uradite je bio da kliknete na kontrolu kojoj zelite da date fokus,
sto moze biti problem korisniku vaseg programa. Druga stvar koju ste mozda primetili je da sam promenio
boju prozora-roditelja u sivu umesto bele koja je bila u prethodnim primerima. Ovo sam uradio da bi se boja
konrola poklopila sa bojom naseg prozora. Postoji nacin da se ovo resi i menjanjem boje kontrola ali to nije
jednostavno jer morate "subclass"-ovati sve kontrole vaseg prozora-roditelja.
Ova otezavajuca okolnost postoji zato sto su kontrole pravljene da bi se koristile sa dijalozima a ne sa
prozorima stvorenim API pozivima. Boja kontrola je siva zato sto je i boja dijaloga siva pa se utapaju jedno
u drugo bez problema i bez potrebe da programer radi bilo sta.
Pre nego sto predjemo na detalje prvo treba da razjasnimo sta je to dijalog (dialog box). Dijalog nije nista
drugo do obicnog prozora koji je dizajniran tako da radi sa kontrolama. Windows takodje obezbedjuje i
predefinisanu proceduru jednog takvog prozora sto mozemo videti po tome da se windows brinu o Tab-u,
Enter-u i jos nekim stvarima cak i ako mi to nismo programirali. Glavna primena dijaloga je za ulaz/izlaz
podataka iz programa. Dijalozi su na neki nacin crne kutije jer ih mozemo koristiti cak i ako ne znamo kako
oni interno funkcionisu vec samo kako da komuniciramo sa njima. Ovo je princip objektnog programiranja
koji se zove skrivanje informacija :) Ako je crna kutija "savrsena" korisnik je moze upotrebljavati bez ikakvog
znanja o nacinu njenog rada. Stvar je u tome da crna kutija mora biti savrsena, a to je tesko ostvarivo u
realnim uslovima. Win32 API je takodje dizajniran kao crna kutija.
Cini se da smo skrenuli sa nase teme. Vratimo se na stvar. Dijalozi su napravljeni da bi smanjili rad
programera. Ako samo napravite prozor koristeci CreateWindowEx i zatim stvarite kontrole na njega,
morate takodje i subklasovati ove kontrole kako bi napisali svu logiku oko njih tipa navigacije pri pritisku na
Tab i slicno. Zato kada koristite dijaloge sve sto treba da znate je kako da komunicirate sa njima. Sav ostali
posao obavlja Windows za vas.
Dijalog je definisan kao resurs na nacin slican nacinu na koji je definisan jedan meni. Dijalog spremite
opisujuci njegove karakteristike i zatim kompajlirajuci resurs skriptu.
Svi resursi se nalaze zajedno u jednoj resurs skripti. Mozete koristiti bilo koji tekst editor da napisete opis
dijaloga ali ja to ne preporucujem. Bolje je da koristite neki vizuelni editor jer je slaganje kontrola po
prozoru tesko ciniti rucno. Postoji par odlicnih editora. Vecina kompajlera ima neki svoj vizuelni editor.
Mozete koristiti njih za stvaranje skripti za vas program a zatim izbrisati djubre koje je kompajler dodao, a
koje pri tome nema veze sa vasim resursima, kao na primer sve stvari vezane za MFC.
Postoje dva tipa dijaloga: "modal" i "modeless". Kada se pokrene modless dijalog, kod glavnog dela
programa nastavlja da se izvrsava paralelno sa kodom dijaloga i pored toga dijalog vam dopusta i da
promenite fokus na neki drugi prozor iste ili druge aplikacije. Primer ovoga je Find dijalog Word-a. Postoje
dve podvrste modal dijaloga: system modal i application modal dijalozi. Razlika je u tome sto sa modal
dijalogom aplikacije vi ne mozete menjati fokus od prozora do prozora u vasem programu ali mozete preneti
fokus na prozor nekog drugog programa, dok sa sistemskim modal dijalogom vi ne mozete pomeriti fokus
sa ovog dijaloga.
Modless dijalozi se stvaraju pozivom na CreateDialogParam API funkciju. Modal dijalozi se stvarju pozivom
na DialogBoxParam. Jedina razlika izmedju modal dijaloga aplikacije i sistema je u tome sto sistemski modal
dijalog ima DS_SYSMODAL stil. Ako ukljucite DS_SYSMODAL stil u vas dijalog onda ce on biti sistemsi modal
dijalog.
Mozete komunicirati sa bilo kojom kontrolom dijaloga koristeci SendDlgItemMessage funkciju. Njena
sintaksa je sldeca:
SendDlgItemMessage proto hwndDlg:DWORD,\
idControl:DWORD,\
uMsg:DWORD,\
wParam:DWORD,\
lParam:DWORD
Ovaj API poziv je prilicno koristan za interakciju sa kontrolama. Na primer, ako zelite da dodjete do teksta iz
polja za unos teksta uradicete sledece:
call SendDlgItemMessage, hDlg, ID_EDITBOX, WM_GETTEXT, 256, ADDR text_buffer
Da bi znali koju poruku da posaljete prverite vasu Win32 API dokumentaciju.

Windows takodje daje i nekoliko API funkcija specificnih za odredjene kontrole. Primer ovakvih funkcija je
GetDlgItemText, CheckDlgButton itd. Ove funkcije su tu da programer ne bi non-stop morao da proveravao
u dokumentaciji sta sve wParam i lParam parametri poruke nose. Koristite ovakve funkcije sto vise i
pribegavajte SendDlgItemMessage funkciji samo kada ne postoji drugo resenje.
Windows salje poruke koje se ticu dijaloga dijalog proceduri koja ima sledeci format:
DlgProc proto hDlg:DWORD ,\
iMsg:DWORD ,\
wParam:DWORD ,\
lParam:DWORD
Dijalog procedura je veoma slicna window proceduri. Jedina razlika je u tome sto dijalog procedura vraca
TRUE/FALSE vrednosti umesto LRESULT. Winodws se bavi svim dijalog porukama u internoj window
proceduri dijaloga i takodje zove nasu dijalog proceduru da bi mi mogli da dodamo akcije. Ako nasa dijalog
procedura odgovara ne neku poruku onda treba da vrati TRUE, a u suprotnom FALSE. Dijalog procedura ne
treba da zove DefWindowProc zato sto nije prava window procedura.
Postoje dva nacina na koja mozemo koristiti dijaloge. Jedan je da oni budu glavni prozori nase aplikacije, a
drugi je da ih koristiko za unos podataka. U ovoj lekciji cemo se pozabaviti sa onim prvim.
"Koriscenje dijaloga kao glavnog prozora" se moze odraditi na dva nacina.
1. Jedan nacin je da koristite dijalog kao normalan prozor tako sto cete registrovati tip klase po uzoru na
dijalog sa RegisterClassEx pozivom. Ovako ce dijalog primati poruke kroz window proceduru datu u
lpfnWndProc clanu klase prozora, a ne kroz dijalog proceduru. Dobra strana ovoga je da ne morate
sami stvarati kontrole vec ce to Windows odratiti za vas i takodje to sto ce se Windows baviti
tastaturom (npr. Tab navigacija). Pored ovoga, mozete takodje sami odrediti kursor i ikonu prozora u
strukuturi klase.
2. Vas program samo stvara dijalog bez pravljenja bilo kakvog roditelja. Ovaj nas pristup lisava potrebe
za glavnom petljom jer ona postoji vec u Windowsu koji poruke salje dijalog proceduri. Cak ne morete
da registrujete ni klasu prozora.
Ovo ce biti dugacka lekcija u kojoj cemo se baviti sa oba pristupa.
Primeri:
dialog.asm
.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
.data
ClassName db "DLGCLASS",0
MenuName db "MyMenu",0
DlgName db "MyDialog",0
AppName db "Our First Dialog Box",0
TestString db "Wow! I'm in an edit box now",0
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
buffer db 512 dup(?)
.const
IDC_EDIT equ 3000

IDC_BUTTON equ 3001


IDC_EXIT equ 3002
IDM_GETTEXT equ 32000
IDM_CLEAR equ 32001
IDM_EXIT equ 32002
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hDlg:HWND
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,DLGWINDOWEXTRA
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_BTNFACE+1
mov wc.lpszMenuName,OFFSET MenuName
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
invoke CreateDialogParam,hInstance,ADDR DlgName,NULL,NULL,NULL
mov hDlg,eax
invoke ShowWindow, hDlg,SW_SHOWNORMAL
invoke UpdateWindow, hDlg
invoke GetDlgItem,hDlg,IDC_EDIT
invoke SetFocus,eax
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke IsDialogMessage, hDlg, ADDR msg
.IF eax ==FALSE
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDIF
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL

.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF lParam==0
.IF ax==IDM_GETTEXT
invoke GetDlgItemText,hWnd,IDC_EDIT,ADDR buffer,512
invoke MessageBox,NULL,ADDR buffer,ADDR AppName,MB_OK
.ELSEIF ax==IDM_CLEAR
invoke SetDlgItemText,hWnd,IDC_EDIT,NULL
.ELSE
invoke DestroyWindow,hWnd
.ENDIF
.ELSE
mov edx,wParam
shr edx,16
.IF dx==BN_CLICKED
.IF ax==IDC_BUTTON
invoke SetDlgItemText,hWnd,IDC_EDIT,ADDR TestString
.ELSEIF ax==IDC_EXIT
invoke SendMessage,hWnd,WM_COMMAND,IDM_EXIT,0
.ENDIF
.ENDIF
.ENDIF
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
Dialog.rc
#include "resource.h"
#define IDC_EDIT 3000
#define IDC_BUTTON 3001
#define IDC_EXIT 3002
#define IDM_GETTEXT 32000
#define IDM_CLEAR 32001
#define IDM_EXIT 32003
MyDialog DIALOG 10, 10, 205, 60
STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX |
WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK
CAPTION "Our First Dialog Box"
CLASS "DLGCLASS"
BEGIN
EDITTEXT IDC_EDIT,15,17,111,13, ES_AUTOHSCROLL | ES_LEFT
DEFPUSHBUTTON "Say Hello", IDC_BUTTON, 141,10,52,13
PUSHBUTTON "E&xit", IDC_EXIT, 141,26,52,13,WS_GROUP
END
MyMenu
MENU
BEGIN
POPUP "Test Controls"
BEGIN
MENUITEM "Get Text", IDM_GETTEXT
MENUITEM "Clear Text", IDM_CLEAR
MENUITEM "", , 0x0800 /*MFT_SEPARATOR*/
MENUITEM "E&xit", IDM_EXIT

END
END
Analiza:
Analizirajmo prvi primer.
Ovaj primer pokazuje kako se koristi dijalog za stvaranje klase prozora i kako se stvara prozor od te klase.
Ovo pojednostavljuje vas program zato sto ne morate da pravite kontrole sami.
Prvo cemo analizirati dijalog.
MyDialog DIALOG 10, 10, 205, 60
Definisite ime dijaloga, u ovom slucaju, "MyDialog" preceno sa komandom "DIALOG". Sledeca cetiri broja su
x, y, sirina i visina dijaloga u jedinicama dijaloga (ne u pikselima!).
STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX |
WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK
Definisete stil dijaloga.
CAPTION "Our First Dialog Box"
Ovo je tekst koji ce biti u naslovu dijaloga.
CLASS "DLGCLASS"
Ovo je bitna linija. Ova CLASS komanda omogucava da koristimo dijalog za klasu. Odmah za tom
komandom dolazi naziv klase.
BEGIN
EDITTEXT IDC_EDIT,15,17,111,13, ES_AUTOHSCROLL | ES_LEFT
DEFPUSHBUTTON "Say Hello", IDC_BUTTON, 141,10,52,13
PUSHBUTTON "E&xit", IDC_EXIT, 141,26,52,13
END
Ovaj je blok definisao kontrole dijaloga. One su definisane izmedju BEGIN i END komandi. Evo kako izgleda
sintaksa:
control-type "text" ,controlID, x, y, width, height [,styles]
control-types su konstante resurs kompajlera i njih cete pronaci u prirucniku istog.
A sada cemo pogledati assembly kod. Zanimljiv deo je u strukturi klase prozora:
mov wc.cbWndExtra,DLGWINDOWEXTRA
mov wc.lpszClassName,OFFSET ClassName
Ovaj bi clan bio ostavljen NULL ali ako zelimo da napravimo i registrujemo klasu koristeci dijalog za primer
onda moramo staviti DLGWINDOWEXTRA u taj clan. Ime klase mora biti identicno imenu koje smo dali u
skripti. Ostali clanovi su podeseni kao u slucaju obicnog prozora. Posto popunite strukturu mozete
registrovati klasu sa RegisterClassEx. Ovu funkciju smo zvali i kada smo registrovali klasu obicnog prozora.
invoke CreateDialogParam,hInstance,ADDR DlgName,NULL,NULL,NULL
Nakon registrovanja klase stvaramo dijalog. U ovom primeru stvaram modeless tip dijaloga koristeci
CreateDialogParam funkciju. Ova funkcija prima 5 parametara ali cemo mi popuniti samo prva dva:
identifikaciju programa (instance handle) i pointer ka imenu dijaloga. Napomena: drugi parametar nije
pointer ka imenu klase vec ka imenu dijaloga.
Sada imamo dijalog i njegove kontrole stvorene od strane Windows-a. Vasa procedura ce primiti
WM_CREATE poruku kao i obicno.
invoke GetDlgItem,hDlg,IDC_EDIT
invoke SetFocus,eax
Posto je dijalog stvoren zelimo da damo fokus polju za unos teksta. Ako kod stavimo u WM_CREATE sekciju
procedure, GetDlgItem poziv nece uspeti zato sto tada kontrole jos uvek nisu stvorene. Jedini nacin da ovo
postignemo je da to uradimo nakon sto su i dijalog i sve kontrole stvorene. Zato stavljam ove dve linije
nakon UpdateWindow poziva. GetDlgItem funkcija uzima ID kontrole i vraca handle svog prozora. Evo kako
doci do handle-a ako znamo ID kontrole.
invoke IsDialogMessage, hDlg, ADDR msg
.IF eax ==FALSE
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDIF
Program ulazi u glavnu petlju u kojoj prima poruke i pre nego sto ih prevede i prosledi proceduri zovemo
IsDialogMessage funkciju da bi dali priliku proceduri Windows-a da se pozabavi sa tastaturom umesto nas.
Ako ova funkcija vrati TRUE to znaci da je poruka namenjena dijalogu i da je obradjuje Windows-ova
procedura. Ovde vidimo jos jednu razliku ove i prethodne lekcije. Kada window procedura zeli da dodje do

teksta kontrole ona zove GetDlgItemText funkciju umesto GetWindowText. GetDlgItemText prima ID
kontrole umesto handle-a prozora kontrole. Ovo pojednostavljuje poziv u slucajevima kada koristite dijaloge.
Sada cemo da vidimo drugi pristup koriscenja dijaloga kao glavnog prozora. U ovom primeru cu napraviti
modal dijalog. Nece videti nikakvu glavnu petlju niti window proceduru jer te stvari nisu potrebne!
dialog.asm(part 2)
.386
.model flat,stdcall
option casemap:none
DlgProc proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
.data
DlgName db "MyDialog",0
AppName db "Our Second Dialog Box",0
TestString db "Wow! I'm in an edit box now",0
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
buffer db 512 dup(?)
.const
IDC_EDIT equ 3000
IDC_BUTTON equ 3001
IDC_EXIT equ 3002
IDM_GETTEXT equ 32000
IDM_CLEAR equ 32001
IDM_EXIT equ 32002
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke DialogBoxParam, hInstance, ADDR DlgName,NULL, addr DlgProc, NULL
invoke ExitProcess,eax
DlgProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_INITDIALOG
invoke GetDlgItem, hWnd,IDC_EDIT
invoke SetFocus,eax
.ELSEIF uMsg==WM_CLOSE
invoke SendMessage,hWnd,WM_COMMAND,IDM_EXIT,0
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF lParam==0
.IF ax==IDM_GETTEXT
invoke GetDlgItemText,hWnd,IDC_EDIT,ADDR buffer,512
invoke MessageBox,NULL,ADDR buffer,ADDR AppName,MB_OK
.ELSEIF ax==IDM_CLEAR
invoke SetDlgItemText,hWnd,IDC_EDIT,NULL
.ELSEIF ax==IDM_EXIT
invoke EndDialog, hWnd,NULL
.ENDIF
.ELSE
mov edx,wParam
shr edx,16
.IF dx==BN_CLICKED

.IF ax==IDC_BUTTON
invoke SetDlgItemText,hWnd,IDC_EDIT,ADDR TestString
.ELSEIF ax==IDC_EXIT
invoke SendMessage,hWnd,WM_COMMAND,IDM_EXIT,0
.ENDIF
.ENDIF
.ENDIF
.ELSE
mov eax,FALSE
ret
.ENDIF
mov eax,TRUE
ret
DlgProc endp
end start
dialog.rc(part 2)
#include "resource.h"
#define IDC_EDIT 3000
#define IDC_BUTTON 3001
#define IDC_EXIT 3002
#define IDR_MENU1 3003
#define IDM_GETTEXT 32000
#define IDM_CLEAR 32001
#define IDM_EXIT 32003
MyDialog DIALOG 10, 10, 205, 60
STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX |
WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK
CAPTION "Our Second Dialog Box"
MENU IDR_MENU1
BEGIN
EDITTEXT IDC_EDIT,15,17,111,13, ES_AUTOHSCROLL | ES_LEFT
DEFPUSHBUTTON "Say Hello", IDC_BUTTON, 141,10,52,13
PUSHBUTTON "E&xit", IDC_EXIT, 141,26,52,13
END
IDR_MENU1 MENU
BEGIN
POPUP "Test Controls"
BEGIN
MENUITEM "Get Text", IDM_GETTEXT
MENUITEM "Clear Text", IDM_CLEAR
MENUITEM "", , 0x0800 /*MFT_SEPARATOR*/
MENUITEM "E&xit", IDM_EXIT
END
END
Analiza sledi:
DlgProc proto :DWORD,:DWORD,:DWORD,:DWORD
Definisemo DlgProc funkciju da bi mogli da koristimo ADDR operator u sledecoj liniji:
invoke DialogBoxParam, hInstance, ADDR DlgName,NULL, addr DlgProc, NULL
Ova linija zove DialogBoxParam funkciju koja prima 5 parametara: identifikaciju programa (instance
handle), ime dijaloga, handle roditelja, adresu dijalog procedure i ekstra parametar koji mozemo koristiti po
volji. Kako je DialogBoxParam stvorio modal dijalog ta se funkcija nece okoncati sve dok se ne unisti
dijalog.
.IF uMsg==WM_INITDIALOG
invoke GetDlgItem, hWnd,IDC_EDIT
invoke SetFocus,eax
.ELSEIF uMsg==WM_CLOSE
invoke SendMessage,hWnd,WM_COMMAND,IDM_EXIT,0

Dijalog procedura, nasuprot window proceduri ne prima WM_CREATE poruku. Prva poruka koju ce dijalog
procedura primiti ce biti WM_INITDIALOG i u tu sekciju smestamo sav kod zaduzen za inicijalizaciju. Ako
koristite ovu poruku morate vratiti TRUE.
Interna procedura dijaloga ne salje WM_DESTROY poruku nasoj proceduri tako da ako zelimo da
reagujemo kada korisnik pritisne X da bi zatvorio prozor, moramo se pozabaviti sa WM_CLOSE porukom. U
nasem primeru saljemo WM_COMMAND poruku sa IDM_EXIT vrednoscu u wParam parametru. Ovo ima isti
efekat kao da je koristnik izabrao Exit iz menija. EndDialog je pozvan kao odgovor na IDM_EXIT.
Obrada podataka pod WM_COMMAND sekcijom ostaje ista.
Jedini nacin da unistite dijalog je da pozovete EndDialog funkciju. Ne pokusavajte da zovete
DestroyWindow! EndDialog ne unistava dijalog odmah. Ona samo javlja internoj proceduri dijaloga da treba
unistiti prozor ali zatim nasavlja sa sledecom instrukcijom.
Ajmo sada da pogledamo resurs fajl. Primetna promena je da smo umesto koriscenja teksta za ime menija
mi koristili numericku vrednost, IDR_MENU1. Ovo je neophodno ako zelite da zakacite meni dijalogu koji je
stvoren sa DialogBoxParam funkcijom. U resurs skripti morate dodati i komandu MENU pracenu sa ID meni
resursa.
Razlika dva primera iz ove lekcije koju mozete odmah primetiti je nedostatak ikonice u drugom primeru.
Ikonu mozete podesiti slanjem WM_SETICON poruke u WM_INITDIALOG sekcije.

Lekcija 11: Jos Malo O Dijalozima


Sada cemo nauciti jos neke stvari oko dijaloga. Videcemo kako se dijalog koristi za unos i izlaz podataka.
Ako ste procitali prethodnu lekciju ova ce vam biti kratka zato sto su bile potrebne samo sitne izmene da bi
mogli da koristimo dijaloge uz nas glavni prozor. Takodje cemo u ovoj lekciji nauciti da koristimo
standardne dijaloge.
Teorija:
Nema puno toga da se kaze na ovu temu. Vas program stvara glavni prozor kao i obicno i kada zelite da
prikazete dijalog pozivate CreateDialogParam ili DialogBoxParam. Posle DialogBoxParam poziva vise nista ne
morate da radite sem da odgovarate na poruke u proceduri dijaloga. Ako koristite CreateDialogParam
morate da ubacite IsDialogMessage u glavnu petlju da bi dali priliku predefinisanoj proceduri dijaloga da se
pozabavi sa porukama sto dolaze od tastature. Necu staviti ovde inzvorni kod. Skinite primere pa ga
pogledajte i ispitajte sami. Ovde mozete naci primer 1 i primer 2 .
Sada malo o standardnim dijalozima (common dialogs). Windows sadrzi nekoliko predefinisanih dijaloga koje
mozemo koristiti u nasim programima. Ovi dijalozi postoje kako bi se standardizovao korisnicki interfejs. Oni
se sadrze od dijaloga sto se bave fajlovima, stampanjem, bojom, fontom i pretragom. Koristite ih
maksimalno. Ovi se dijalozi nalaze u comdlg32.dll. Da bi ih koristili morate da vezete program sa
comdlg32.lib . Ove dijaloge stvaramo tako sto pozivamo odgovarajuce funkcije ovog DLL-a. Za "Open File"
dijalog funkcija se zove GetOpenFileName, za "Save File" to je GetSaveFileName, za stamanje je PrintDlg i
tako dalje. Svaka od ovih funkcija prima pointer ka nekoj svojoj strukturi. Strukture mozete potraziti u vasoj
Win32 API dokumentaciji. U ovoj lekciji cemo se pozabaviti sa stvaranjem i koriscenjem "Open File" dijaloga.
Evo prototipa GetOpenFileName funkcije:
GetOpenFileName proto lpofn:DWORD
Mozete da vidite da prima samo jedan parametar - pointer ka OPENFILENAME strukturi. Ako je povratna
vrednost funkcije TRUE to znaci da je korisnik izabrao fajl za otvaranje; u suprotnom nije. Sada cemo
pogledati OPENFILENAME strukturu.
OPENFILENAME STRUCT
lStructSize DWORD ?
hwndOwner HWND ?
hInstance HINSTANCE ?
lpstrFilter LPCSTR ?
lpstrCustomFilter LPSTR ?
nMaxCustFilter DWORD ?
nFilterIndex DWORD ?
lpstrFile LPSTR ?
nMaxFile DWORD ?
lpstrFileTitle LPSTR ?
nMaxFileTitle DWORD ?
lpstrInitialDir LPCSTR ?
lpstrTitle LPCSTR ?
Flags DWORD ?
nFileOffset WORD ?
nFileExtension WORD ?
lpstrDefExt LPCSTR ?
lCustData LPARAM ?
lpfnHook DWORD ?
lpTemplateName LPCSTR ?
OPENFILENAME ENDS
Pogledajmo znacenje cesto koriscenih clanova strukture.
lStructSize - velicina OPENFILENAME strukture u bajtovima.
hwndOwner - handle prozora dijaloga.
hInstance - identifikacija (instance handle) programa koji stvara dijalog.
lpstrFilter - string koji se koristi kao filter. Ovaj string je formiran od parova stringova gde se svaki
zavrsava sa NULL karakterom. Prvi string svakog para je opis, a drugi govori koji su tipovi fajlova
dozvoljeni. Na primer:

FilterString db "All Files (*.*)",0, "*.*",0


db "Text Files (*.txt)",0,"*.txt",0,0
Zapazite da Windows koristi samo drugi string svakog para za filtriranje fajlova. Takodje je vazno
napomenuti da treba dodati jedan ekstra NULL karakter na kraju poslednjeg para kako bi se obelezio
kraj.
nFilterIndex - govori koji ce filter biti izabran kada se dijalog pojavi. Ovaj indeks pocinje od 1. Ako
stavimo 2 u nasem primeru, Windows ce koristiti drugi par sa "*.txt" filterom.
lpstrFile - pointer ka memoriji koja sadrzi ime fajla koje ce biti napisano u polju za unos teksta na
dijalogu. Ova memorija bi trebalo da bude barem 260 bajta velika. Posto korisnik izabere fajl koji zeli
da otvori, ime fajla sa punom adresom ce biti na ovom istom mestu u memoriji. Odatle posle mozete
doci do informacija.
nMaxFile - velicina lpstrFile memorije.
lpstrTitle - pointer ka naslovu dijaloga.
Flags - odredjuje stil i karakteristike dijaloga.
nFileOffset - nakon sto korisnik izabere fajl koji zeli da otvori, ovaj clan sadrzi index prvog karaktera
imena fajla (bez ostatka adrese sto mu prethodi). Na primer ako je izabran fajl:
"c:\windows\system\lz32.dll", ovaj ce clan imati vrednost 18.
nFileExtension - posto korisnik izabere fajl koji zeli da otvori ovaj clan sadrzi index prvog karaktera
ekstenzine fajla.
Primer:
Program sto sledi prikazuje dijaloz za otvaranje fajla kada korisnik izabere File-> Open iz menija. Kada
korisnik izabere fajl iz dijaloga, program ce prikazati poruku sa punom adresom fajla, imenom fajla i
ekstenzijom istog.
.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include\masm32\include\windows.inc
include\masm32\include\user32.inc
include\masm32\include\kernel32.inc
include\masm32\include\comdlg32.inc
includelib\masm32\lib\user32.lib
includelib\masm32\lib\kernel32.lib
includelib\masm32\lib\comdlg32.lib
.const
IDM_OPEN equ 1
IDM_EXIT equ 2
MAXSIZE equ 260
OUTPUTSIZE equ 512
.data
ClassName db "SimpleWinClass",0
AppName db "Our Main Window",0
MenuName db "FirstMenu",0
ofn OPENFILENAME <>
FilterString db "All Files",0,"*.*",0
db "Text Files",0,"*.txt",0,0
buffer db MAXSIZE dup(0)
OurTitle db "-=Our First Open File Dialog Box=-: Choose the file to open",0
FullPathName db "The Full Filename with Path is: ",0
FullName db "The Filename is: ",0
ExtensionName db "The Extension is: ",0
OutputString db OUTPUTSIZE dup(0)

CrLf db 0Dh,0Ah,0
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
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 hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,OFFSET MenuName
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
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,300,200,NULL,NULL,\
hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_COMMAND

mov eax,wParam
.if ax==IDM_OPEN
mov ofn.lStructSize,SIZEOF ofn
push hWnd
pop ofn.hwndOwner
push hInstance
pop ofn.hInstance
mov ofn.lpstrFilter, OFFSET FilterString
mov ofn.lpstrFile, OFFSET buffer
mov ofn.nMaxFile,MAXSIZE
mov ofn.Flags, OFN_FILEMUSTEXIST or \
OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
OFN_EXPLORER or OFN_HIDEREADONLY
mov ofn.lpstrTitle, OFFSET OurTitle
invoke GetOpenFileName, ADDR ofn
.if eax==TRUE
invoke lstrcat,offset OutputString,OFFSET FullPathName
invoke lstrcat,offset OutputString,ofn.lpstrFile
invoke lstrcat,offset OutputString,offset CrLf
invoke lstrcat,offset OutputString,offset FullName
mov eax,ofn.lpstrFile
push ebx
xor ebx,ebx
mov bx,ofn.nFileOffset
add eax,ebx
pop ebx
invoke lstrcat,offset OutputString,eax
invoke lstrcat,offset OutputString,offset CrLf
invoke lstrcat,offset OutputString,offset ExtensionName
mov eax,ofn.lpstrFile
push ebx
xor ebx,ebx
mov bx,ofn.nFileExtension
add eax,ebx
pop ebx
invoke lstrcat,offset OutputString,eax
invoke MessageBox,hWnd,OFFSET OutputString,ADDR AppName,MB_OK
invoke RtlZeroMemory,offset OutputString,OUTPUTSIZE
.endif
.else
invoke DestroyWindow, hWnd
.endif
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
Analiza:
mov ofn.lStructSize,SIZEOF ofn
push hWnd
pop ofn.hwndOwner
push hInstance
pop ofn.hInstance
Popunjavamo elemente ofn strukture.

mov ofn.lpstrFilter, OFFSET FilterString


FilterString je filter koji koristimo, a evo i njegove deklaracije:
FilterString db "All Files",0,"*.*",0
db "Text Files",0,"*.txt",0,0
Zapazite da su sva cetiri stringa zavrsena sa NULL karakterom. Prvi string je opis narednog stringa. Stvarni
filteri se nalaze u parnim stringovima, u ovom slucaju su to "*.*" i "*.txt". Ovde mozemo staviti koji god tip
zelimo. Na kraju svih stringova moramo staviti i jedan dodatan NULL karakter koji oznacava kraj. Ako ovo
zaboravite vas ce se dijalog cudno ponasati.
mov ofn.lpstrFile, OFFSET buffer
mov ofn.nMaxFile,MAXSIZE
Odredjujemo gde ce dijalog sacuvati ime fajla koji korisnik zeli da otvori. Moramo javiti dijalogu velicinu ove
memorije kroz nMaxFilter element strukture. Posle mozemo doci do imena fajla jednostavno citajuci taj deo
memorije.
mov ofn.Flags, OFN_FILEMUSTEXIST or \
OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
OFN_EXPLORER or OFN_HIDEREADONLY
Flags sadrzi karakteristike dijaloga.
OFN_FILEMUSTEXIST i OFN_PATHMUSTEXIST zahtevaju da adresa plus ime fajla koje korisnik upise u polje
za unos teksta MORAJU postojati.
OFN_LONGNAMES govori dijalogu da prikazuje dugacka imena.
OFN_EXPLORER govori dijalogu da prikaz mora biti nalik exploreru.
OFN_HIDEREADONLY skriva read-only polje.
Postoji jos puno stilova koje kozete koristiti i svi se nalaze u vasoj Win32 API dokumentaciji.
mov ofn.lpstrTitle, OFFSET OurTitle
Naslov dijaloga.
invoke GetOpenFileName, ADDR ofn
Poziv ka GetOpenFileName funkciji sa pointerom ka ofn strukturi.
Sada je dijalog prikazan na ekranu. Funkcija se nece okoncati sve dok korisnik ne izabere neki fajl ili stisne
Cancel kako bi zatvorio dijalog.
Vratice TRUE u eax-u ako je korisnik izabrao neki fajl ili FALSE ako nije.
.if eax==TRUE
invoke lstrcat,offset OutputString,OFFSET FullPathName
invoke lstrcat,offset OutputString,ofn.lpstrFile
invoke lstrcat,offset OutputString,offset CrLf
invoke lstrcat,offset OutputString,offset FullName
U slucaju da korisnik izabere fajl za otvaranje pripremicemo string koji ce biti prikazan u MessageBox-u.
Alociramo blok memorije kroz OutputString promenljivu i tada koristimo API funkciju lstrcat koja ce spojiti
stringove. Da bi svaki string bio prikazan u jednom redu moramo ih odvojiti sa 2 karaktera koja obelezavaju
novi red.
mov eax,ofn.lpstrFile
push ebx
xor ebx,ebx
mov bx,ofn.nFileOffset
add eax,ebx
pop ebx
invoke lstrcat,offset OutputString,eax
Ove linije zahtevaju objasnjenje. nFileOffset sadrzi indeks iz ofn.lpstrFile. Ali ne mozete ih spojiti direktno
zato sto je nFileOffset promenjiva velika 2 bajta (WORD) a lpstrFile je DWORD. Zato morate da stavite
vrednost iz nFileOffset u bx i zatim dodate ebx na lpstrFile.
invoke MessageBox,hWnd,OFFSET OutputString,ADDR AppName,MB_OK
Prikazujemo string koristeci MessageBox.
invoke RtlZerolMemory,offset OutputString,OUTPUTSIZE
Moramo *ocistiti* OutputString pre nego sto ga popunimo sa drugim stringom pa zato koristimo
RtlZeroMemory funkciju da obavi posao.

Lekcija 12: O Memoriji I Fajlovima


U ovoj lekciji cemo uciti o koriscenju memorije i fajlova. Koristicemo i neke standardne dijaloge za ulaz-izlaz
podataka. Skinite primer .
Teorija:
Koriscenje memorije pod Win32 je veoma jednostavno kada se gleda sa strane aplikacije. Svaki proces
moze da cita i pise u rasponu od 4 GB memorije. Ovaj memorijski model se zove FLAT model. U ovom
modelu svi segmentski registri pokazuju na istu pocetnu vrednost, a offset je 32-bitni broj tako da program
moze pristupiti svoj memoriji bez potrebe da menja segmentske registre. Ovo znatno pojednostavljuje
programiranje. Takodje, pointeri su jednostavno pointeri, ne postoje vise niti "near" niti "far" vrste pointera.
Pod Win16 postoje dve glavne kategorije API funkcija koje se bave memorijom: globalne (Global) i lokalne
(Local). Globalne su se bavile sa memorijom alociranom u drugim segmentima tako da su bile "far"
funkcije. Lokalne su se bavile sa lokalnom memorijom procesa tako da su bile "near" funkcije. Pod Win32
ova dva tipa su identicna. Bilo da zovete GlobalAlloc ili LocalAlloc dobicete isti rezultat.
Koraci za alociranje i koriscenje memorije su sledeci:
1. Alociranje bloka memorije pozivajuci GlobalAlloc. Ova funkcija vraca handle trazenog memorijskog
bloka.
2. Zakljucavanje bloka pozivajuci GlobalLock. Ova funkcija prima handle ka memoriji i vraca pointer ka
istoj.
3. Koriscenje pointera za citanje i pisanje po memoriji.
4. Odkljucavanje memorije pozivom na GlobalUnlock. Ova funkcija ponistava pointer ka memoriji.
5. Oslobadjanje memorije pozivom ka GlobalFree. Ova funkcija prima handle memorijskog bloka.
Takodje mozete zameniti "Global" sa "Local", npr. LocalAlloc, LocalLock itd.
Gore navedeni metod moze biti pojednostavljen koristeci GMEM_FIXED za tip trazenog bloka kroz
GlobalAlloc poziv. Ako to uradite GlobalAlloc ce vratiti pointer ka memoriji umesto handle-a tako da ne
morate zvati GlobalLock i GlobalUnlock. Kroz ovu lekciju cu ipak koristiti "tradicionalni" pristup jer mozete
naici na njega citajuci izvorni kod drugih programa.
Koriscenje fajlova pod Win32 je slicno onome pod DOS-om. Koraci su isti, samo treba zameniti interrupt
pozive sa API pozivima. Evo kako se to radi:
1. Otvora se ili se kreira novi fajl sa CreateFile funkcijom. Ova funkcija pored fajlova moze da otvara i
komunikacione portove, cevi (pipes), diskove i konzole. Po uspehu vraca handle ka otvorenom
objektu. Zatim se ovaj handle moze koristiti za obavljanje operacija na objektu.
2. Pomera se pointer fajla na zeljenu lokaciju pozivajuci SetFilePointer.
3. Obavlja se citanje ili pisanje pozivajuci ReadFile ili WriteFile. Ove funkcije kopiraju informacije iz fajla u
neki nas deo memorije ili kopiraju deo memorije u fajl. Ovo znaci da treba alocirati dovoljno memorije
tako da sve informacije iz fajla mogu da stanu u nju.
4. Zatvaranje fajla pozivom na CloseHandle. Ova funkcija prima handle fajla.
Sadrzaj:
Program sto vidite nize prikazuje "Open File" dijalog. On dopusta korisniku da izabere tekstualni fajl koji zeli
da otvori i zatim prikazuje sadrzaj fajla u polju za unos teksta. Korisnik moze da modifikuje ovaj tekst i da
ga sacuva ako hoce.
.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\comdlg32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

includelib \masm32\lib\comdlg32.lib
.const
IDM_OPEN equ 1
IDM_SAVE equ 2
IDM_EXIT equ 3
MAXSIZE equ 260
MEMSIZE equ 65535
EditID equ 1 ; ID of the edit control
.data
ClassName db "Win32ASMEditClass",0
AppName db "Win32 ASM Edit",0
EditClass db "edit",0
MenuName db "FirstMenu",0
ofn OPENFILENAME <>
FilterString db "All Files",0,"*.*",0
db "Text Files",0,"*.txt",0,0
buffer db MAXSIZE dup(0)
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hwndEdit HWND ? ; Handle to the edit control
hFile HANDLE ? ; File handle
hMemory HANDLE ? ;handle to the allocated memory block
pMemory DWORD ? ;pointer to the allocated memory block
SizeReadWrite DWORD ? ; number of bytes actually read or write
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:SDWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
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 hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,OFFSET MenuName
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
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,300,200,NULL,NULL,\
hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW mov eax,msg.wParam
ret
WinMain endp
WndProc proc uses ebx hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_CREATE
invoke CreateWindowEx,NULL,ADDR EditClass,NULL,\
WS_VISIBLE or WS_CHILD or ES_LEFT or ES_MULTILINE or\
ES_AUTOHSCROLL or ES_AUTOVSCROLL,0,\
0,0,0,hWnd,EditID,\
hInstance,NULL
mov hwndEdit,eax
invoke SetFocus,hwndEdit
;==================================================
; Initialize the members of OPENFILENAME structure
;==================================================
mov ofn.lStructSize,SIZEOF ofn
push hWnd
pop ofn.hWndOwner
push hInstance
pop ofn.hInstance
mov ofn.lpstrFilter, OFFSET FilterString
mov ofn.lpstrFile, OFFSET buffer
mov ofn.nMaxFile,MAXSIZE
.ELSEIF uMsg==WM_SIZE
mov eax,lParam
mov edx,eax
shr edx,16
and eax,0ffffh
invoke MoveWindow,hwndEdit,0,0,eax,edx,TRUE
.ELSEIF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.if lParam==0
.if ax==IDM_OPEN
mov ofn.Flags, OFN_FILEMUSTEXIST or \

OFN_PATHMUSTEXIST or OFN_LONGNAMES or\


OFN_EXPLORER or OFN_HIDEREADONLY
invoke GetOpenFileName, ADDR ofn
.if eax==TRUE
invoke CreateFile,ADDR buffer,\
GENERIC_READ or GENERIC_WRITE ,\
FILE_SHARE_READ or FILE_SHARE_WRITE,\
NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,\
NULL
mov hFile,eax
invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,MEMSIZE
mov hMemory,eax
invoke GlobalLock,hMemory
mov pMemory,eax
invoke ReadFile,hFile,pMemory,MEMSIZE-1,ADDR SizeReadWrite,NULL
invoke SendMessage,hwndEdit,WM_SETTEXT,NULL,pMemory
invoke CloseHandle,hFile
invoke GlobalUnlock,pMemory
invoke GlobalFree,hMemory
.endif
invoke SetFocus,hwndEdit
.elseif ax==IDM_SAVE
mov ofn.Flags,OFN_LONGNAMES or\
OFN_EXPLORER or OFN_HIDEREADONLY
invoke GetSaveFileName, ADDR ofn
.if eax==TRUE
invoke CreateFile,ADDR buffer,\
GENERIC_READ or GENERIC_WRITE ,\
FILE_SHARE_READ or FILE_SHARE_WRITE,\
NULL,CREATE_NEW,FILE_ATTRIBUTE_ARCHIVE,\
NULL
mov hFile,eax
invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,MEMSIZE
mov hMemory,eax
invoke GlobalLock,hMemory
mov pMemory,eax
invoke SendMessage,hwndEdit,WM_GETTEXT,MEMSIZE-1,pMemory
invoke WriteFile,hFile,pMemory,eax,ADDR SizeReadWrite,NULL
invoke CloseHandle,hFile
invoke GlobalUnlock,pMemory
invoke GlobalFree,hMemory
.endif invoke SetFocus,hwndEdit
.else
invoke DestroyWindow, hWnd
.endif
.endif
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
Analiza:
invoke CreateWindowEx,NULL,ADDR EditClass,NULL,\
WS_VISIBLE or WS_CHILD or ES_LEFT or ES_MULTILINE or\

ES_AUTOHSCROLL or ES_AUTOVSCROLL,0,\
0,0,0,hWnd,EditID,\
hInstance,NULL
mov hwndEdit,eax
U WM_CREATE sekciji stvaramo polje za unos teksta. Parametri za poziciju i velicinu kontrole su sve nule jer
cemo kasnije podesiti velicinu tako da pokriva ceo prozor.
Takodje ne zovemo ShowWindow da bi prikazali polje za unos teksta zato sto smo u stil stavili WS_VISIBLE
sto automatski prikazuje prozore. Ovo smo isto uradili i za glavni prozor.
;=================================================
; Initialize the members of OPENFILENAME structure
;=================================================
mov ofn.lStructSize,SIZEOF ofn
push hWnd
pop ofn.hWndOwner
push hInstance
pop ofn.hInstance
mov ofn.lpstrFilter, OFFSET FilterString
mov ofn.lpstrFile, OFFSET buffer
mov ofn.nMaxFile,MAXSIZE
Posle stvaranja kontrole spremamo ofn strukturu. Posto cemo istu strukturu koristiti i za cuvanje i otvaranje
fajlova popunicemo samo zajednicke elemente koje ce koristiti GetOpenFileName i GetSaveFileName.
WM_CREATE sekcija je odlicno mesto za vrsenje raznih inicijalizacija.
.ELSEIF uMsg==WM_SIZE
mov eax,lParam
mov edx,eax
shr edx,16
and eax,0ffffh
invoke MoveWindow,hwndEdit,0,0,eax,edx,TRUE
Kada se menja velicina naseg glavnog prozora primamo WM_SIZE poruku. Istu poruku cemo primiti i kada
se prozor pojavi po prvi put. Da bi mogao da prima takve poruke prozor mora imati CS_VREDRAW i
CS_HREDRAW stil. Ovu priliku koristimo da promenimo velicinu naseg polja za unos teksta koje ce zauzeti
celu radnu povrsinu glavnog prozora. Potrebne informacije primamo kroz lparam. Hight word lParam
parametra sadrzi visinu, a low word sirinu radne povrsine prozora. Zatim ove informacije koristimo
pozivajuci MoveWindow funkciju koja pored toga sto menja poziciju prozora takodje menja i njegovu
velicinu.
.if ax==IDM_OPEN
mov ofn.Flags, OFN_FILEMUSTEXIST or \
OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
OFN_EXPLORER or OFN_HIDEREADONLY
invoke GetOpenFileName, ADDR ofn
Kada korisnik izabere File/Open meni ispunicemo Flag element ofn strukture i pozvacemo GetOpenFileName
kako bi prikazali dijalog za otvaranje fajlova.
.if eax==TRUE
invoke CreateFile,ADDR buffer,\
GENERIC_READ or GENERIC_WRITE ,\
FILE_SHARE_READ or FILE_SHARE_WRITE,\
NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,\
NULL
mov hFile,eax
Posto korisnik izabere fajl za otvaranje pozivamo CreateFile da bi otvorili fajl. Dajemo funkciji do znanja da
treba da pokusa ta otvori fajl za citanje i pisanje. Kada se fajl otvori funkcija vraca handle otvorenog fajla
koji cuvamo u globalnoj promenljivoj za kasniju upotrebu. Evo i sintakse ove funkcije:
CreateFile proto lpFileName:DWORD,\
dwDesiredAccess:DWORD,\
dwShareMode:DWORD,\
lpSecurityAttributes:DWORD,\
dwCreationDistribution:DWORD\,

dwFlagsAndAttributes:DWORD\,
hTemplateFile:DWORD
dwDesiredAccess Odredjuje koje ce operacije biti vrsene nad fajlom.
0 - otvara fajl za ucitavanje atributa. Potrebno je da imate pravo da pisete ili citate informacije iz fajla.
GENERIC_READ - otavara fajl za citanje.
GENERIC_WRITE - otvara fajl za pisanje.
dwShareMode Odredjuje koje ce operacije neki drugi proces moci da obavlja nad fajlom koji smo mi
otvorili.
0 - fajl se nece deliti sa drugim procesima.
FILE_SHARE_READ - dopusta drugim procesima da citaju informacije iz otvorenog fajla.
FILE_SHARE_WRITE - dopusta drugim procesima da pisu u otvoreni fajl.
lpSecurityAttributes Nije bitno na Windows 95.
dwCreationDistribution Odredjuje akciju koju ce CreateFile obaviti nad fajlom ako on postoji ili ne postoji.
CREATE_NEW - stvara novi fajl. Funkcija se nece okoncati uspesno ako fajl vec postoji.
CREATE_ALWAYS - stvara novi fajl. Ako postoji fajl funkcija ce pisati preko njega.
OPEN_EXISTING - otvara fajl. Funkcija se nece okoncati uspesno ako fajl ne postoji.
OPEN_ALWAYS - otvara fajl ako postoji. Ako ne postoji funkcija ce ga stvoriti kao da smo odredili
CREATE_NEW akciju.
TRUNCATE_EXISTING Otvara fajl. Kada ga otvori brise sve iz njega. Proces sto ovo zeli da uradi mora
imati barem GENERIC_WRITE pristup. Funkcija se nece okoncati uspesno ako fajl ne postoji.
dwFlagsAndAttributes Odredjuje atribute fajla.
FILE_ATTRIBUTE_ARCHIVE - fajl je arhiva. Aplikacije koriste ovaj atribut za obelezavanje fajlova za
brisanje ili za sigurnosne kopije.
FILE_ATTRIBUTE_COMPRESSED - fajl ili direktorijum je komresovan. Za fajl ovo znaci da su sve
informacije u fajlu kompresovane. Za direktorijum ovo znaci da je kompresija standard za novo
stvorene fajlove.
FILE_ATTRIBUTE_NORMAL - fajl nema nikakve dodatne atribute. Ovaj atribut je vazeci jedino ako se
koristi samostalno.
FILE_ATTRIBUTE_HIDDEN - fajl je skriven i nece biti ukljucen u obicnu listu direktorijuma.
FILE_ATTRIBUTE_READONLY - fajl je samo za citanje. Aplikacije mogu da citaju informacije iz ovog
fajla ali ga ne mogu menjati ili brisati.
FILE_ATTRIBUTE_SYSTEM - fajl je deo operativnog sistema ili ga ekskluzivno koristi operativni sistem.
invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,MEMSIZE
mov hMemory,eax
invoke GlobalLock,hMemory
mov pMemory,eax
Kada se fajl otvori alociramo blok memorije koji cemo koristiti sa ReadFile i WriteFile funkcijama. Kao tip
bloka stavljamo GMEM_MOVEABLE sto ce dopustiti Windowsu da pomera blok po memoriji kako bi je
stedeo sto vise. GMEM_ZEROINIT govori GlobalAlloc funkciji da memoriju ispuni nulama.
Kada se GlobalAlloc uspesno okonca eax ce biti handle memorijskog bloka. Njega cemo proslediti
GlobalLock funkciji koja ce nam dati pointer ka memoriji.
invoke ReadFile,hFile,pMemory,MEMSIZE-1,ADDR SizeReadWrite,NULL
invoke SendMessage,hwndEdit,WM_SETTEXT,NULL,pMemory
Kada je memorija spremna za upotrebu pozivamo ReadFile koji ce da popuni nasu memoriju sa
informacijama iz fajla. Kada je fajl otvoren ili stvoren, fajl pointer je 0. U ovom slucaju ce znaci citanje fajla
poceti od prvog bajta. Prvi parametar ReadFile funkcije je handle fajla, drugi je broj bajta koji treba da
procita, zatim, adresa promenjive koja ce primiti broj bajta procitanih iz fajla.
Posto popunimo memoriju sa informacijama, stavicemo ih u polje za unos teksta tako sto cemo poslati toj
kontroli WM_SETTEXT poruku sa lParam parametrom koji ce biti pointer ka memoriji sa informacijama.

Posle ovog poziva ce polje za unos teksta biti ispunjeno tekstom iz fajla.
invoke CloseHandle,hFile
invoke GlobalUnlock,pMemory
invoke GlobalFree,hMemory
.endif
Sada vise ne moramo da drzimo fajl otvoren jer zelimo da modifikovan tekst sacuvamo u drugi fajl. Fajl
zatvaramo sa CloseHandle pozivom ciji je parametar handle fajla. Zatim odkljucavamo i oslobadjamo
memoriju. U stvari, ne morate da oslobadjate memoriju sada jer je mozete ponovo koristiti kasnije za
cuvanje fajla. U primeru je oslobadjam cisto radi demonstracije.
invoke SetFocus,hwndEdit
Kada se otvari dijalog za otvaranje fajla, fokus ce otici sa polja na unos teksta na ovaj dijalog tako da po
njegovom zatvaranju treba da vratimo fokus polju za unos teksta.
Ovo je kraj operacije citanja fajla. Sada korisnik moze da menja tekst. Kada bude zeleo da sacuva promene
ici ce na File/Save koji ce prikazati dijalog za cuvanje fajlova. Pravljenje ovog dijaloga nije puno drugacije
od pravljenja dijaloga za otvaranje fajlova. U stvari je jedina razlika u funkciji koju pozivamo. Vecinu
elemenata ofn strukture mozete koristiti ponovo bez ikakvih promena. Jedine promene su izvrsene u Flag
elementu.
mov ofn.Flags,OFN_LONGNAMES or\
OFN_EXPLORER or OFN_HIDEREADONLY
U nasem slucaju zelimo da stvorimo novi fajl tako da OFN_FILEMUSTEXIST i OFN_PATHMUSTEXIST
izostavljamo jer u suprotnom fajl nece moci da se stvori ako fajl sa takvim imenom vec ne postoji u
direktorijumu u kome se nalazimo.
dwCreationDistribution parametar CreateFile funkcije se mora promeniti u CREATE_NEW jer zelimo da
stvorimo novi fajl.
Ostatak koda je slican onome sto smo koristili za otvaranje fajla izuzev sledeceg:
invoke SendMessage,hwndEdit,WM_GETTEXT,MEMSIZE-1,pMemory
invoke WriteFile,hFile,pMemory,eax,ADDR SizeReadWrite,NULL
Saljemo WM_GETTEXT poruku polju za unos teksta da bi kopirali informacije u nas blok memorije. Vrednost
u eax-u ce biti velicina informacija u bajtima. Posto se informacije kopiraju u memorijski blok, kopiramo ga u
novi fajl.

Lekcija 13: Mapiranje Fajlova U Memoriji


Pokazacu vam sta su fajlovi mapirani u memoriji i kako se koristite. U ovoj lekciji cete videti da je koriscenje
ovakvih fajlova veoma jenostavno. Skinite primer.
Teorija:
Ako pazljivo proucite primer iz prethodne lekcije primeticete da ima jednu veliku manu. Zapitacete se sta ce
se desiti ako je fajl veci od alocirane memorije? Ili sta ako je string koji trazite ostao bez svoje druge
polovine usled kraja memorijskog bloka? Tradicionalno resenje za prvi problem je da treba da citate
informacije iz fajla malo po malo sve dok ne naidjete na kraj bloka. Resenje za drugi problem je da treba
da napisete kod za takav slucaj. Ovo se zove "boundary value problem" i pretsavlja pravu glavobolju za
programere i izvor nebrojenih bagova.
Bilo bi lepo kada bi mogli da alociramo veoma veliki memorijski blok, dovoljan da sadrzi ceo fajl ali ce onda
nas program da izcrpi sve resurse. Ovde dolazi mapiranje fajla. Kada koristite mapirani fajl treba da mislite
o njemu kao da je on vec ceo ucitan u memoriju i da koristite pointer da citate ili pisete u njega. Nema vise
potrebe za koriscenje API funkcija za ulaz/izlaz podataka iz fajla i funkcija za memoriju. Sve je to ujedinjeno
kod mapiranja. Mapiranje fajlova je takodje korisno i kod razmene podataka izmedju procesa. Koristeci
mapiranje za ovo ne zahteva nikakav pravi fajl vec je vise nalik rezervisanoj memoriji koju svaki proces
*vidi*. Ipak, mapiranje virtuelnog fajla zarad razmene informacija izmedju procesa je prilicno komplikovana
tema jer procesi moraju biti sinhronizovani inace ce program da *pukne* za tili cas.
U ovoj lekciji se necemo doticati stvaranja mapiranog fajla zbog razmene informacija medju procesima vec
cemo se koncentrisati na mapiranje postojeceg fajla. PE loaderi koriste mapiranje fajla za ucitavanje
modula. To je prilicno prakticno jer su samo neophodni delovi ucitavaju u memoriju u svakom datom
trenutku. Pod Win32 treba koristiti ovo sto je vise moguce.
Postoje neka ogranicenja pri mapiranju fajlova. Kada ih jednom otvorite njihova se velicina ne moze menjati
dok su otvoreni. Ovo znaci da je mapiranje odlicno za citanje informacija iz fajla ali nije prakticno ako
pisemo u fajl pri cemu se njegova velicina moze promeniti. Ovo mozemo prevazici tako sto cemo prvo
izracunati velicinu fajla nakon pisanja pa zatim mapirati fajl u veci blok memorije sto ce automatski povecati
fajl. Prilicno ne prakticno kao sto vidite.
Dosta objasnjavanja. Zaronimo u koriscenje ove tehnike. Da bi se koristilo mapiranje fajlova potrebno je
uraditi sledece:
1. pozovite CreateFile da otvorite fajl koji zelite da mapirate.
2. pozovite CreateFileMapping sa handle-om sto je vratio CreateFile kao jednim od parametara. Ova
funkcija stvara objekat koji predstavlja mapirani fajl koji je otvoren sa CreateFile.
3. pozovite MapViewOfFile da mapirate izabrani deo fajla ili ceo fajl. Ova funkcija vraca pointer ka prvom
bajtu mapirane regije.
4. Koristite pointer da citate ili pisete po fajlu.
5. pozovite UnmapViewOfFile da zatvorite mapiranje.
6. pozovite CloseHandle sa handle-om mapiranog fajla da bi ga zatvorili.
7. pozovite ponovo CloseHandle ali ovaj put sa handle-om sto je vratio CreateFile da bi zatvorili i sam
fajl.
Primer:
Ovaj program vam dopusta da otvorite fajl putem standardnog dijaloga. Fajl se nakon otvaranja mapira i
ako je to proslo uspesno, naslov prozora se menja u naziv otvorenog fajla. Mozete sacuvati fajl u neki drugi
koristeci File/Save As meni. Program ce koporati celokupan sadrzaj u drugi fajl. Zapazite da ovde nema
potrebe za GlobalAlloc.
.386
.model flat,stdcall
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\comdlg32.inc
includelib \masm32\lib\user32.lib

includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\comdlg32.lib
.const
IDM_OPEN equ 1
IDM_SAVE equ 2
IDM_EXIT equ 3
MAXSIZE equ 260
.data
ClassName db "Win32ASMFileMappingClass",0
AppName db "Win32 ASM File Mapping Example",0
MenuName db "FirstMenu",0
ofn OPENFILENAME & lt;>
FilterString db "All Files",0,"*.*",0
db "Text Files",0,"*.txt",0,0
buffer db MAXSIZE dup(0)
hMapFile HANDLE 0 ; Handle to the memory mapped file, must be
; initialized with 0 because we also use it as
; a flag in WM_DESTROY section too
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hFileRead HANDLE ? ; Handle to the source file
hFileWrite HANDLE ? ; Handle to the output file
hMenu HANDLE ?
Memory DWORD ? ; pointer to the data in the source file
SizeWritten DWORD ? ; number of bytes actually written by WriteFile
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
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 hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,OFFSET MenuName
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
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,\
ADDR AppName, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,300,200,NULL,NULL,\
hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_CREATE
invoke GetMenu,hWnd ; Obtain the menu handle
mov hMenu,eax
mov ofn.lStructSize,SIZEOF ofn
push hWnd
pop ofn.hWndOwner
push hInstance
pop ofn.hInstance
mov ofn.lpstrFilter, OFFSET FilterString
mov ofn.lpstrFile, OFFSET buffer
mov ofn.nMaxFile,MAXSIZE
.ELSEIF uMsg==WM_DESTROY
.if hMapFile!=0
call CloseMapFile
.endif
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.if lParam==0
.if ax==IDM_OPEN
mov ofn.Flags, OFN_FILEMUSTEXIST or \
OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
OFN_EXPLORER or OFN_HIDEREADONLY
invoke GetOpenFileName, ADDR ofn
.if eax==TRUE
invoke CreateFile,ADDR buffer,\
GENERIC_READ ,\
0,\
NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,\
NULL

mov hFileRead,eax
invoke CreateFileMapping,hFileRead,NULL,PAGE_READONLY,0,0,NULL
mov hMapFile,eax
mov eax,OFFSET buffer
movzx edx,ofn.nFileOffset
add eax,edx
invoke SetWindowText,hWnd,eax
invoke EnableMenuItem,hMenu,IDM_OPEN,MF_GRAYED
invoke EnableMenuItem,hMenu,IDM_SAVE,MF_ENABLED
.endif
.elseif ax==IDM_SAVE
mov ofn.Flags,OFN_LONGNAMES or\
OFN_EXPLORER or OFN_HIDEREADONLY
invoke GetSaveFileName, ADDR ofn
.if eax==TRUE
invoke CreateFile,ADDR buffer,\
GENERIC_READ or GENERIC_WRITE ,\
FILE_SHARE_READ or FILE_SHARE_WRITE,\
NULL,CREATE_NEW,FILE_ATTRIBUTE_ARCHIVE,\
NULL
mov hFileWrite,eax
invoke MapViewOfFile,hMapFile,FILE_MAP_READ,0,0,0
mov pMemory,eax
invoke GetFileSize,hFileRead,NULL
invoke WriteFile,hFileWrite,pMemory,eax,ADDR SizeWritten,NULL
invoke UnmapViewOfFile,pMemory
call CloseMapFile
invoke CloseHandle,hFileWrite
invoke SetWindowText,hWnd,ADDR AppName
invoke EnableMenuItem,hMenu,IDM_OPEN,MF_ENABLED
invoke EnableMenuItem,hMenu,IDM_SAVE,MF_GRAYED
.endif
.else
invoke DestroyWindow, hWnd
.endif
.endif
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
CloseMapFile PROC
invoke CloseHandle,hMapFile
mov hMapFile,0
invoke CloseHandle,hFileRead
ret
CloseMapFile endp
end start
Analiza:
invoke CreateFile,ADDR buffer,\
GENERIC_READ ,\
0,\
NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,\

NULL
Kada korisnik izabere fajl za otvaranje putem standardnog dijaloga mi pozivamo CreateFile da bi ga otvorili.
Zapazite da smo fajl otvorili koristeci GENERIC_READ sto ga otvara samo za citanje, i sa nulom za
dwShareMode zato sto necemo da neki drugi proces modifikuje fajl dok se mi bavimo sa njim.
invoke CreateFileMapping,hFileRead,NULL,PAGE_READONLY,0,0,NULL
Zatim zovemo CreateFileMapping za stvaranje objekta. Evo i sintakse ove funkcije:
CreateFileMapping proto hFile:DWORD,\
lpFileMappingAttributes:DWORD,\
flProtect:DWORD,\
dwMaximumSizeHigh:DWORD,\
dwMaximumSizeLow:DWORD,\
lpName:DWORD
Treba da znate da CreateFileMapping ne mora da mapira ceo fajl. Mozete koristiti ovu funkciju da mapirate
samo deo fajla. Broj bajtova koji ce se mapirati u memoriji su odredjeni sa dwMaximumSizeHight i
dwMaximumSizeLow parametrima. Ako date broj veci od stvarne velicine fajla, fajl ce biti uvecan do te nove
granice. Ako zelite da mapirate ceo fajl i da ne bude nikakvog viska pozovite funkciju i stavite nule na
mestu ova dva parametra.
Mozete koristiti NULL kod lpFileMappingAtributes parametra i dobicete standardan set atributa.
flProtect definise zastitu za mapirani fajl. U nasem primeru koristimo PAGE_READONLY sto nam dozvoljava
samo da citamo iz mapiranog fajla. Vazno je napomenuti da ovaj atribut ne sme da dolazi u sukob sa
slicnim atributom iz CreateFile funkcije inace CreateFileMapping nece uspeti.
lpName pointer ka imenu mapiranog fajla. Ako zelite da delite ovaj fajl sa drugim procesima onda mu
morate dodeliti neko ime. U nasem primeru samo nas jedan proces treba da koristi fajl tako da smo
ignorisali ovaj parametar.
mov eax,OFFSET buffer
movzx edx,ofn.nFileOffset
add eax,edx
invoke SetWindowText,hWnd,eax
Ako je CreateFileMapping okoncan uspesno, menjamo naslov prozora u ime otvorenog fajla. Ime fajla sa
njegovom punom adresom se nalazi u bufferu. Posto zelimo da prikazemo samo ime moramo dodati
nFileOffset element OPENFILENAME strukture adresi buffera.
invoke EnableMenuItem,hMenu,IDM_OPEN,MF_GRAYED
invoke EnableMenuItem,hMenu,IDM_SAVE,MF_ENABLED
Zbog predostroznosti necemo otvarati vise od jednog fajla u isto vreme tako da iskljucujemo Open meni, a
ukljucujemo Save. EnableMenuItem je funkcija koju koristimo u ovu svrhu.
Nakon ovoga cekamo da korisnik izabere File/Save As meni ili da ugasi nas program. Ako korisnik zeli da
ugasi program prvo moramo da zatvorimo mapiranje fajla i sam fajl kao u prilozenom:
.ELSEIF uMsg==WM_DESTROY
.if hMapFile!=0
call CloseMapFile
.endif
invoke PostQuitMessage,NULL
Videli smo da kada windowprocedura prima WM_DESTROY poruku proveravamo vrednost hMapFile
promenljive da vidimo da li je nula ili ne. Ako nije nula zovemo CloseMapFile:
CloseMapFile PROC
invoke CloseHandle,hMapFile
mov hMapFile,0
invoke CloseHandle,hFileRead
ret
CloseMapFile endp
CloseMapFile zatvara mapirani fajl i sam fajl tako da nece biti gubitaka resurasa kada se program napokon
okonca.
Ako korisnik izabere da sacuva tekst u drugi fajl program prikazuje standardni dijalog. Posto korisnik ukuca
ime novog fajla, fajl se stvara CreateFile pozivom.
invoke MapViewOfFile,hMapFile,FILE_MAP_READ,0,0,0
mov pMemory,eax
Cim je fajl stvoren zovemo MapViewOfFile da bi mapirali fajl u memoriju. Evo sintakse:

MapViewOfFile proto hFileMappingObject:DWORD,\


dwDesiredAccess:DWORD,\
dwFileOffsetHigh:DWORD,\
dwFileOffsetLow:DWORD,\
dwNumberOfBytesToMap:DWORD
dwDesiredAccess je operacija koju zelimo da obavimo na fajlu. U nasem primeru zelimo samo da
citamo informacije tako da koristimo FILE_MAP_READ.
dwFileOffsetHigh, dwFileOffsetLow predstavlja pocetni bajt od koga zelimo da pocnemo mapiranje. U
nasem slucaju zelimo da procitamo ceo fajl tako da koristimo nulu.
dwNumberOfBytesToMap predstavlja broj bajtova za mapiranje. Ako zelite da mapirate ceo fajl
(odredjen sa CreateFileMapping), koristite nulu.
Posle MapViewOfFile zeljeni fajl je mapiran i dat nam je pointer ka memoriji koja sadrzi informacije iz fajla.
invoke GetFileSize,hFileRead,NULL
Pronalazimo velicinu fajla. Velicina fajla ce se nalaziti u eax registru. Ako je fajl veci od 4 GB, high DWORD
se nalazi u FileSizeHighDword. Kako ne ocekujemo da radimo sa tako velikim fajlovima samo cemo
ignorisati ovaj parametar.
invoke WriteFile,hFileWrite,pMemory,eax,ADDR SizeWritten,NULL
Prepisite informacije iz mapiranog fajla u novi fajl.
invoke UnmapViewOfFile,pMemory
Zavrsili smo sa unosom informacija tako da zatvaramo mapiranje.
call CloseMapFile
invoke CloseHandle,hFileWrite
I naravno zatvaramo sve fajlove.
invoke SetWindowText,hWnd,ADDR AppName
Vracamo pocetni naslov prozoru.
invoke EnableMenuItem,hMenu,IDM_OPEN,MF_ENABLED
invoke EnableMenuItem,hMenu,IDM_SAVE,MF_GRAYED
Ukljucjemo Open meni, a iskljucujemo Save As.

Lekcija 14: Procesi


Naucicemo sta je to proces, kako se proces pokrece i kako prekida. Skinite primer.
Uvod:
Pa, sta je to proces? Prepisacu ono sto je napisano u Win32 API dokumentaciji:
"Proces je program u radu koji se sastoji od privatnog virtuelnog prostora u memoriji, koda, informacija i
drugih resurasa operativnog sistema kao sto su fajlovi, cevi i objekti za sinhronizaciju koji su vidljivi tom
procesu."
Kao sto mozete videti iz prethodne definicije, proces "poseduje" nekoliko objekata: prostor u memoriji,
izvrsne module i sve sto ti moduli stvore ili otvore. Jedan proces ce se barem sastojati od nesto memorije,
od izvrshnog modula i jednog thread-a (niti egzekucije). Sta je thread? Thread je u stvari jedan
podprogram. Ako proces ima vise ovih objekata, oni mogu raditi paralelno jedan sa drugim kao sto vise
programa moze da radi paralelno. Kada windows pokrenu proces, automatksi stvore i jedan thread koji ce
poceti od prve instrukcije programa. Proces moze nakon toga stvoriti jos threadova ako su mu potrebni.
Kada Windows dobije komandu da stvori proces, prvo stvara privatan prostor u memoriji za taj proces i
onda mapira izvrsni modul u taj prostor. Posle toga stvara glavni thread za taj proces.
Pod Win32 takodje mozete stvarati procese iz svojih programa koristeci CreateProcess funkciju.
CreateProces ima sledecu sintaksu:
CreateProcess proto lpApplicationName:DWORD,\
lpCommandLine:DWORD,\
lpProcessAttributes:DWORD,\
lpThreadAttributes:DWORD,\
bInheritHandles:DWORD,\
dwCreationFlags:DWORD,\
lpEnvironment:DWORD,\
lpCurrentDirectory:DWORD,\
lpStartupInfo:DWORD,\
lpProcessInformation:DWORD
Nemojte se uplasiti od braja parametara. Vecinu njih mozemo prosto ignorisati.
lpApplicationName Ime EXE fajla koji zelite da pokrenete, sa ili bez pune adrese. Ako je ovaj
parametar NULL, EXE mora biti u lpCommandLine parametru.
lpCommandLine Parametri za komandnu liniju. Ako je lpApplication NULL ovaj parametar mora da
sadrzi i ime EXE-a kao na primer: "notepad.exe readme.txt"
lpProcessAttributes, lpthreadAttributes Sigurnostni atributi procesa i glavnog thread-a. Ako su NULL
onda dobijaju standardni set atributa.
bInheritHandles Odredjuje da li ce novi proces nasledi sve otvorene handle-ove vaseg procesa.
dwCreationFlags Ove vrednosti odredjuju ponasanje procesa koji zelite da stvorite, kao na primer, da
li zelite da se proces stvori pa zatim momentalno pauzira tako da ga mozete ispitati ili modifikovati pre
njegovog pokretanja. Takodje mozete dati i prioritet izvrsavanja threadova u novo strovernom
procesu. Uglavnom se koristi NORMAL_PRIORITY_CLASS vrednost.
lpEnvironment Pointer ka bloku sa stringovima koji opisuju orkuzenje procesa. Ako je ovo NULL, novi
proces ce naslediti ovaj blok od vaseg procesa.
lpCurrentDirectory Pointer ka stringu koji sadrzi trenutan radni direktorijum novog procesa. Ako je ovo
NULL, novi proces ce koristiti trenutni radni direktorijum vaseg procesa.
lpStartupInfo Pointer ka STARTUPINFO strukturi koja odredjuje kako ce glavni prozor novog procesa
biti prikazan. STARTUPINFO struktura ima dosta elemenata koji odredjuju nacin prikazivanja glavnog
prozora novog procesa. Ako vam nije potrebno nista posebno samo ispunite STARTUPINFO strukturu
sa vrednostima vaseg procesa koje mozete dobiti pozivom GetStartupInfo funkcije.
lpProcessInformation Pointer ka PROCESS_INFORMATION strukturi koja prima identifikacione
informacije novog procesa. PROCESS_INFORMATION struktura se sastoji od sledecih elemenata:
PROCESS_INFORMATION STRUCT
hProcess HANDLE ? ; handle procesa
hThread HANDLE ? ; handle glavnog threada
dwProcessId DWORD ? ; identifikacija (ID) procesa
dwThreadId DWORD ? ; identifikacija glavnog threada
PROCESS_INFORMATION ENDS

Proces handle i proces ID su dve razlicite stvari. Proces ID je jedinstveni identifikacioni broj procesa. Proces
handle je vrednost koju je Windows nama dao za koriscenje drugih API funkcija koje traze ovu vrednost.
Proces handle se ne moze koristiti za identifikaciju procesa zato sto nije jedinstven.
Nakon CreateProcess poziva novi proces je stvoren i CreateProcess funkcija se okoncava. Mozete proveriti
da li je proces jos uvek aktivan pozivajuci GetExitCodeProcess funkciju. Sintaksa funkcije sledi.
GetExitCodeProcess proto hProcess:DWORD, lpExitCode:DWORD
Ako je ovaj poziv uspesan, lpExitCode ce sadrzati status procesa. Ako je vrednost STILL_ACTIVE to znaci da
se proces jos uvek nije okoncao.
Mozete naterati proces da se ugasi pozivajuci TerminateProcess funkciju. Ona ima sledecu sintaksu:
TerminateProcess proto hProcess:DWORD, uExitCode:DWORD
Mozete odrediti i zeljeni izlazni kod procesa i to moze biti bilo koja vrednost. TerminateProcess nije cist
nacin prekidanja procesa zato sto DLL-ovi zakaceni za proces nece dobiti obavestenje da je proces
okoncan.
Primer:
Primer koji sledi ce stvoriti novi proces kada korisnik bude izabrao "create process" iz menija. Probace da
pokrene "msgbox.exe". Ako korisnik bude zeleo da prekine proces moze da izabere "terminate process" iz
menija. Program ce prvo proveriti da li je proces trenutno aktivan, pa ako jeste onda ce pozvati
TerminateProcess kako bi ga prekinuo.
.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
.const
IDM_CREATE_PROCESS equ 1
IDM_TERMINATE equ 2
IDM_EXIT equ 3
.data
ClassName db "Win32ASMProcessClass",0
AppName db "Win32 ASM Process Example",0
MenuName db "FirstMenu",0
processInfo PROCESS_INFORMATION <>
programname db "msgbox.exe",0
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hMenu HANDLE ?
ExitCode DWORD ? ; contains the process exitcode status from GetExitCodeProcess call.
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT

invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
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 hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,OFFSET MenuName
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
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,300,200,NULL,NULL,\
hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
invoke GetMenu,hwnd
mov hMenu,eax
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL startInfo:STARTUPINFO
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_INITMENUPOPUP
invoke GetExitCodeProcess,processInfo.hProcess,ADDR ExitCode
.if eax==TRUE
.if ExitCode==STILL_ACTIVE

invoke EnableMenuItem,hMenu,IDM_CREATE_PROCESS,MF_GRAYED
invoke EnableMenuItem,hMenu,IDM_TERMINATE,MF_ENABLED
.else
invoke EnableMenuItem,hMenu,IDM_CREATE_PROCESS,MF_ENABLED
invoke EnableMenuItem,hMenu,IDM_TERMINATE,MF_GRAYED
.endif
.else
invoke EnableMenuItem,hMenu,IDM_CREATE_PROCESS,MF_ENABLED
invoke EnableMenuItem,hMenu,IDM_TERMINATE,MF_GRAYED
.endif
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.if lParam==0
.if ax==IDM_CREATE_PROCESS
.if processInfo.hProcess!=0
invoke CloseHandle,processInfo.hProcess
mov processInfo.hProcess,0
.endif
invoke GetStartupInfo,ADDR startInfo
invoke CreateProcess,ADDR programname,NULL,NULL,NULL,FALSE,\
NORMAL_PRIORITY_CLASS,\
NULL,NULL,ADDR startInfo,ADDR processInfo
invoke CloseHandle,processInfo.hThread
.elseif ax==IDM_TERMINATE
invoke GetExitCodeProcess,processInfo.hProcess,ADDR ExitCode
.if ExitCode==STILL_ACTIVE
invoke TerminateProcess,processInfo.hProcess,0
.endif
invoke CloseHandle,processInfo.hProcess
mov processInfo.hProcess,0
.else
invoke DestroyWindow,hWnd
.endif
.endif
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
Analiza:
Program stvara glavni prozor i dolazi do meni handle za kasniju upotrebu. Zatim ceka da korisnik izabere
komandu iz menija. Kada izabere "Process" iz glavnog menija obradjujemo WM_INITPOPUP poruku i
modifikujemo meni pre nego sto je on prikazan.
.ELSEIF uMsg==WM_INITMENUPOPUP
invoke GetExitCodeProcess,processInfo.hProcess,ADDR ExitCode
.if eax==TRUE
.if ExitCode==STILL_ACTIVE
invoke EnableMenuItem,hMenu,IDM_CREATE_PROCESS,MF_GRAYED
invoke EnableMenuItem,hMenu,IDM_TERMINATE,MF_ENABLED
.else
invoke EnableMenuItem,hMenu,IDM_CREATE_PROCESS,MF_ENABLED
invoke EnableMenuItem,hMenu,IDM_TERMINATE,MF_GRAYED
.endif
.else

invoke EnableMenuItem,hMenu,IDM_CREATE_PROCESS,MF_ENABLED
invoke EnableMenuItem,hMenu,IDM_TERMINATE,MF_GRAYED
.endif
Zasto se osvrcemo na ovo poruku? Zato sto zelimo da pripremimo meni pre nego sto se on prikaze
korisniku. U nasem primeru, ako novi proces jos nije pokrenut zelimo da "start process" bude slobodan, a
"terminate process" zakljucan; obratno ako je proces vec aktivan.
Prvo proveravamo da li je proces jos uvek aktivan pozivajuci GetExitCodeProcess funkciju, sa handle-om
procesa koji smo dobili pozivajuci CreateProcess. Ako GetExitCodeProcess vrati FALSE proces nije pokrenut
pa zakljucavamo "terminate process" element menija. Ako GetExitCodeProcess vrati TRUE znamo da je
proces bio porkenut ali moramo da proverimo i ExitCode. Ako je STILL_ACTIVE onda je proces aktivan pa
treba da zakljucamo "start process" jer ne zelimo da pokrenemo vise od jednog procesa.
.if ax==IDM_CREATE_PROCESS
.if processInfo.hProcess!=0
invoke CloseHandle,processInfo.hProcess
mov processInfo.hProcess,0
.endif
invoke GetStartupInfo,ADDR startInfo
invoke CreateProcess,ADDR programname,NULL,NULL,NULL,FALSE,\
NORMAL_PRIORITY_CLASS,\
NULL,NULL,ADDR startInfo,ADDR processInfo
invoke CloseHandle,processInfo.hThread
Kada korisnik izabere "start process" prvo proveravamo da li je hProcess element PROCESS_INFORMATION
strukture zatvoren. Ako je ovo prvi put da se pokrece proces, hProcess ce biti 0 jer smo celu strukturu
stavili u .data sekciju. Ako ova vrednost nije 0 to znaci da je proces zavrsen, a mi jos uvek nismo zatvorili
handle tako da cemo to sada da ucinimo.
Mozemo pozvati GetStartupInfo da popunimo startupinfo strukturu koju cemo proslediti CreateProcess
funkciji, koja ce pokrenuti novi proces. Nisam proverio povratnu vrednost CreateProcess funkcije zato sto bi
to ucinilo primer kompleksnijim. U pravom programu ovo treba proveriti. Odmah nakon CreateProcess
zatvaramo handle glavnog threada. Zatvaranje handle ne znaci da okoncavamo thread vec samo da ne
zelimo da koristimo tu vrednost u nasem programu. Ako je ne zatvorimo izazvace gubljenje memorije.
.elseif ax==IDM_TERMINATE
invoke GetExitCodeProcess,processInfo.hProcess,ADDR ExitCode
.if ExitCode==STILL_ACTIVE
invoke TerminateProcess,processInfo.hProcess,0
.endif
invoke CloseHandle,processInfo.hProcess
mov processInfo.hProcess,0
Kada korisnik izabere "terminate process" proveravamo da li je proces jos uvek aktivan pozivajuci
GetExitCodeProcess. Ako je jos uvek aktivan, prekinucemo proces sa TerminateProcess i zatvoricemo i
handle jer nam vise nije potreban.

Lekcija 15: Rad Sa Threadovima (Nitima Egzekucije)


Sada cemo nauciti kako se pisu programi koji rade sa vise threadova i prostudiracemo nacin komunikacije
medju njima. Skinite primer.
Teorija:
U prethodnoj lekciji ste naucili da se proces sastoji od najmanje jednog threada: glavnog ili primarnog
threada. Thread je jedna nit ili lanac egzekucije. Mozete stvarati dodatne threadove u vasem programu.
Mozete gledati na rad sa vise threadova kao na multitasking unutar programa. Kao implementacija, thread
je funkicja koja se izvrsava paralelno sa glavnim programom. U zavisnosti od vasih potreba mozete imati
vise kopija funkcije u memoriji ili vise razlicitih funkcija koje se izvrsavaju istovremeno. Vise threadova je
mogucnost samo Win32 sistema.
Threadovi se izvrsavaju unutar istog procesa tako da imaju pristup svim resursima kao sto su na primer
globalne promenljive, ali zato svaki ima svoj stack pa su lokalne promenljive potpuno privatne. Svaki thread
takodje ima i svoj set registara tako da kada se Windows preorientise na neki drugi thread, ovaj "pamti"
stanje registara i vraca ih u prvobitno stanje kada se Windows ponovo vrati na njega. Ovaj proces se
interno obavlja u Windows-u.
Mozemo podeliti threadove u dve kateogrije:
1. Thread sa korisnickim interfejsom: Ovaj tip stvara svoj prozor i prima poruke. Moze da komunicira sa
korisnikom putem svog prozora i odatle je i dobio ime. Pod Win16 je bilo moguce imati samo jedan
thread sa korisnickim interfejsom pod 16-bitnim jezgrom. Dok bi trhead sa korisnickim interfejsom
izvrsavao instrukcije u 16-bitnom korisnickom i gdi jezgru, drugi threadovi sa korisnickim interfejsom
nisu mogli da koriste usluge jezgra. Ovo je i slucaj sa Windows 95 jer je njegov API na kraju sveden
na 16-bitan kod. Windows NT nema Win16 Mutex tako da threadovi sa korisnickim interfejsom rade
gladje nego po Windows 95.
2. Thread radnik: Ovaj tip ne stvara prozor tako da ne moze ni da prima nikakve poruke. Postoji samo
da bi odradio neki posao u pozadini i po tome je i dobio ime.
Preporucujem sledecu strategiju kada se koristi vise threadova pod Win32: Neka primarni trhead obavlja
sve funkcije korisnickog interfejsa, a drugi neka obavljaju zahtevne poslove u pozadini. Na ovaj nacin se
glavni trhead moze poistovetiti sa guvernerom a svi ostali sa njegovim osobljem. Guverner deli poslove
svom osoblju dok odrzava kontakte sa javnoscu. Osoblje pokorno obavlja posao i odgovara guverneru. Ako
bi guverner sam obavljao poslove ne bi mogao da posveti dovoljno paznje javnosti. Ovo se desava kada
neki prozor programa ne odgovara na zahteve korisnika dok se ne okonca posao zadat programu. Takav
program moze imati benefit od stvaranja jos jednog threada koji ce obavljati dugotrajan posao,
dozvoljavajuci primarnom threadu da se bavi korisnikom.
Thread mozemo napraviti CreateThread funkcijom koja ima sledecu sintaksu:
CreateThread proto lpThreadAttributes:DWORD,\
dwStackSize:DWORD,\
lpStartAddress:DWORD,\
lpParameter:DWORD,\
dwCreationFlags:DWORD,\
lpThreadId:DWORD
CreateThread funkcija dosta lici na CreateProcess.
lpThreadAttributes Mozete koristiti NULL ako zelite da thread ima standardan set sigurnosnih atributa.
dwStackSize Odredite velicinu stack-a ovog threada. Mozete koristiti NULL ako zelite da thread ima
stack iste velicine kao i primarni.
lpStartAddress Adresa funkcije koja predstavlja thread. Ova funkcija ce obavljati sav posao. Ona
MORA da primi jedan jedini 32-bitni parametar i da vrati 32-bitnu vrednost.
lpParameter Parametar koji zelite da se prosledi funkciji.
dwCreationFlags 0 znaci da thread moze da pocne sa radom cim je stvoren. Suprotno tome je
CREATE_SUSPENDED.
lpThreadId Funkcija CreateThread ce na ovu adresu staviti ID novo stvorenog threada.
Ako je CreateThread uspesan, povratna vrednost ce biti handle novog threada, a ako ne onda ce biti NULL.
Thread funkcija ce se pokrenuti cim se CreateThread zavrsi osim ako nismo koristili CREATE_SUSPENDED u
dwCreationFlags. U tom slucaju thread je pauziran sve dok se ne pozove ResumeThread.

Kada se funkcija threada okonca sa ret instrukcijom, Windows poziva ExitThread funkciju za taj thread. I
sami mozete pozvati ExitThread iz vase funkcije threada ali nema posebne potrebe za tim.
Kada se thread okonca on vraca neku vrednost do koje mozete doci GetExitCodeThread funkcijom.
Ako zelite da ugasite jedan thread iz nekog drugog threada, treba da koristite TerminateThread. Ovu
funkciju treba da koristite samo u ekstremnim slucajevima zato sto ona momentalno gasi trhead ne dajuci
mu priliku da oslobodi resurse.
Sada prelazimo na komunikaciju medju threadovima.
Postoje tri nacina:
1. Koriscenje globalnih promenljivih
2. Windows poruke
3. Event (dogadjaj)
Threadovi dele resurse procesa ukljucujuci i globalne promenljive tako da mogu koristiti njih za
komunikaciju. Problem je sto je sinhronizacija veoma bitna. Na primer ako dva threada koriste istu
strukturu od 10 elemenata sta ce se desiti kada Windows iznenada prekine rad jednog threada u sred
popunjavanja strukture, i pocne sa izvrsavanjem drugog? Drugi thread ce biti ostavljen sa ne popunjenom
strukturom! Programi sa vise threadova su tezi za odrzavanje i debugovanje, a problemi kao ovaj se tesko
lociranju.
Takodje mozete koristiti Windows poruke za komunikaciju threadova. Ako su svi threadovi sa korisnickim
interfejsom onda nema problema i ovaj vid kominikacije se moze slobodno koristiti u oba smera. Sve sto
treba da ucinite je da definisete jednu ili vise vasih Windows poruka koje ce imati neko znacenje za vase
threadove. Poruke se definisu tako sto se koristi WM_USER poruka kao osnova. Na primer:
WM_MYCUSTOMMSG equ WM_USER+100h
Windows nece koristiti vrednosti od WM_USER pa na gore za svoje poruke tako da ih vi mozete koristiti za
sta god su vam potrebne.
Ako neki of vasih threadova ima korisnicki interfejs a drugi ne onda ovaj metod ne mozete koristiti za
dvosmernu komunikaciju jer radni thread (onaj bez interfejsa) ne moze da prima poruke ali zato mozete
koristiti ovu kombinaciju:ovo:
Thread sa korisnickim interfejsom ------> globalne promenljive ----> Thread radnik
Thread radnik ------> Windows poruke ----> Thread sa korisnickim interfejsom
Ovaj metod cemo koristiti u nasem primeru.
Poslednji nacin komuniciranja su event objekti (dogadjaji). Na event mozete gledati kao na signalnu
zastavicu. Ako je event ugasen onda thread spava i ne dobija deo procesorskog vremena. Kada se event
aktivira onda i Windows bude thread i on pocinje da obavlja zadatu mu radnju.
Primer:
Ako vec niste, skinite primer i pokrenite thread1.exe. Stisnite "Savage Calculation" u meniju. Ovo ce narediti
programu da izvrsi "add eax, eax" 600,000,000 puta. Za ovo vreme ne mozete raditi nista sa glavnim
prozorom: ne mozete ga pomeriti, koristiti menije itd. Kada se racunanje zavrsi pojavice se MessageBox
nakon cega ce prozor poceti normalno da funkcionise.
Da bi izbegli ovakvo ponasanje prozora pomericemo racunsku operaciju u proceduru koja ce da se izvrsi
kao drugi thread. Ovim cemo dopustiti glavnom threadu da se bavi korisnickim interfejsom. Mozete videti
da cak i ako prozor nesto sporije odgovara neko inace, on ipak odgovara.
.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
.const
IDM_CREATE_THREAD equ 1

IDM_EXIT equ 2
WM_FINISH equ WM_USER+100h
.data
ClassName db "Win32ASMThreadClass",0
AppName db "Win32 ASM MultiThreading Example",0
MenuName db "FirstMenu",0
SuccessString db "The calculation is completed!",0
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hwnd HANDLE ?
ThreadID DWORD ?
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
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 hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,OFFSET MenuName
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
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,300,200,NULL,NULL,\
hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE

invoke GetMessage, ADDR msg,NULL,0,0


.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.if lParam==0
.if ax==IDM_CREATE_THREAD
mov eax,OFFSET ThreadProc
invoke CreateThread,NULL,NULL,eax,\
0,\
ADDR ThreadID
invoke CloseHandle,eax
.else
invoke DestroyWindow,hWnd
.endif
.endif
.ELSEIF uMsg==WM_FINISH
invoke MessageBox,NULL,ADDR SuccessString,ADDR AppName,MB_OK
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
ThreadProc PROC USES ecx Param:DWORD
mov ecx,600000000
Loop1:
add eax,eax
dec ecx
jz Get_out
jmp Loop1
Get_out:
invoke PostMessage,hwnd,WM_FINISH,NULL,NULL
ret
ThreadProc ENDP
end start
Analiza:
Glavni program program ima obican prozor i meni. Ako korisnik izabere "Create Thread" iz menija program
ce stvoriti thread ovako:
.if ax==IDM_CREATE_THREAD
mov eax,OFFSET ThreadProc
invoke CreateThread,NULL,NULL,eax,\
NULL,0,\

ADDR ThreadID
invoke CloseHandle,eax
Ova funkcija stvara thread sto ce paralelno sa primarnim threadom izvrsavati proceduru sa imenom
ThreadProc. Nakon uspesnog poziva, CreateThread se zavrsava i ThreadProc odmah pocinje da radi. Kako
ne koristimo handle threada treba da ga zatvorimo inace ce biti curenja memorije. Zatvaranje ovog handle
ne gasi i thread vec samo znaci da vise ne mozemo koristiti taj handle.
ThreadProc PROC USES ecx Param:DWORD
mov ecx,600000000
Loop1:
add eax,eax
dec ecx
jz Get_out
jmp Loop1
Get_out:
invoke PostMessage,hwnd,WM_FINISH,NULL,NULL
ret
ThreadProc ENDP
Kao sto vidite ThreadProc obavlja racunanje koje zahteva neko vreme, a kada zavrsi salje WM_FINISH
poruku glavnom prozoru. WM_FINISH je poruka koju smo definisali ovako:
WM_FINISH equ WM_USER+100h
Nema potrebe da dodate 100h na WM_USER ali je tako sigurnije.
WM_FINISH poruka nesto znaci samo u nasem programu. Kada glavni prozor primi WM_FINISH poruku on
odgovara prikazivanjem MessageBox-a koji kaze da je racunanje obavljeno.
Mozete stvoriti nekoliko threadova tako sto cete par puta stisnuti "Create Thread".
U ovom primeru komunikacija je jednosmerna. Samo radni thread moze javiti kraj glavnom prozoru tj.
primarnom threadu. Ako zelite da glavni thread nesto javi radnom to mozete uciniti ovako: Dodajte
meni sa imenom "Kill Thread" ili slicno.
Potrebna je globalna promenljiva koja ce se kroistiti kao signalna zastavica. Ako je TRUE znaci da
thread mora da stane, FALSE da moze da nastavi sa radom.
Izmenite ThreadProc tako da u loop-u proverava vrednost ove promenjive.
Kada korisnik izabere "Kill Thread" iz menija, glavni thread ce staviti TRUE u globalnu promenljivu. Kada
radni thread vidi da je ova promenljiva TRUE, izacice iz loop-a i time zavrsiti.

Lekcija 16: Event (Dogadjaj)


Naucicemo sta je to dogadjaj i kako se koristi u programima sa vise niti egzekucije. Skinite primer.
Teorija:
U prosloj lekciji sam demonstrirao kako threadovi (niti egzekucije) komuniciraju preko Window poruka, a
izostavio sam druge dve metode: globalne promenljive i event objekte. Ovaj put cemo videti obe.
Event objekat je kao prekidac sa dve pozicije: ukljuceno i iskljuceno. Kada je event objekat ukljucen
kazemo da je u signalnom stanju. Logicno, kada je iskljucen onda nije u signalnom stanju. Mozete stvoriti
event objekat pa napisati kod u relevantnim threadovima tako da mozete da pratite stanje event objekata.
Ako objekat nije u signalnom stanju, thread koji ceka na njega ce biti uspavan, a kao takav ce koristiti
manje CPU vremena.
Event objekat stvarate pozivajuci CreateEvent funkciju koja ima sledecu sintaksu:
CreateEvent proto lpEventAttributes:DWORD,\
bManualReset:DWORD,\
bInitialState:DWORD,\
lpName:DWORD
lpEventAttribute Ako koristite NULL vrednost, event objekat ce biti stvoren sa predefinisanim
sigurnosnim atributima.
bManualReset Ako zelite da Windows automatsku resetuje event objekat na ne signalno stanje posle
WaitForSingleObject poziva, morate staviti FALSE u ovaj parametar, inace cete morati rucno resetovati
event objekat sa ResetEvent.
bInitialState Ako zelite da event objekat bude stvoren u signalnom stanju, koristite TRUE za ovaj
parametar, inace ce event objekat biti stvoren u ne signalnom stanju.
lpName Pointer ka ASCIIZ stringu sa imenom event objekta. Ovo ime se koristi kada zelite da
pozovete OpenEvent.
Ako je poziv uspesan, povratna vrednost je handle ka novo stvorenom event objektu. U suprotnom ce
povratna vrednost biti NULL.
Mozete menjati stanje event objekta sa dva API poziva: SetEvent i ResetEvent. SetEvent funkcija stavlja
event objekat u signalno stanje, a ResetEvent cini suprotno.
Kada je event objekat stvoren morate staviti poziv ka WaitForSingleObject u thread koji treba da prati
stanje event objekta. WaitForSingleObject ima sledecu sintaksu:
WaitForSingleObject proto hObject:DWORD, dwTimeout:DWORD
hObject Handle ka jednom od sinhronizujucih objekata. Event objekat je jedan od takvih.
dwTimeout vreme u milisekundama koje ce funkcija provesti cekajuci da objekat bude u signalnom
stanju. Ako ovo vreme istekne a objekat jos uvek nije u signalnom stanju, WaitForSingleObject
prestaje sa cekanjem. Ako zelite da ceka neograniceno dugo morate koristiti vrednost INFINITE za
ovaj parametar.
Primer: Ovaj primer prikazuje prozor i ceka da korisnik izabere komandu iz menija. Ako koristnik izabere
"run thread", thread ce poceti sa racunskom operacijom. Kada se ona okonca, pojavice se poruka koja ce
informisati korisnika da je posao obavljen. Za vreme dok je thread jos uvek aktivan, korisnik moze da
izabere "stop thread" iz menija kako bi zaustavio thread.
.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
.const

IDM_START_THREAD equ 1
IDM_STOP_THREAD equ 2
IDM_EXIT equ 3
WM_FINISH equ WM_USER+100h
.data
ClassName db "Win32ASMEventClass",0
AppName db "Win32 ASM Event Example",0
MenuName db "FirstMenu",0
SuccessString db "The calculation is completed!",0
StopString db "The thread is stopped",0
EventStop BOOL FALSE
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hwnd HANDLE ?
hMenu HANDLE ?
ThreadID DWORD ?
ExitCode DWORD ?
hEventStart HANDLE ?
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
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 hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,OFFSET MenuName
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
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,\
ADDR AppName,\

WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,300,200,NULL,NULL,\
hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
invoke GetMenu,hwnd
mov hMenu,eax
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_CREATE
invoke CreateEvent,NULL,FALSE,FALSE,NULL
mov hEventStart,eax
mov eax,OFFSET ThreadProc
invoke CreateThread,NULL,NULL,eax,\
NULL,0,\
ADDR ThreadID
invoke CloseHandle,eax
.ELSEIF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.if lParam==0
.if ax==IDM_START_THREAD
invoke SetEvent,hEventStart
invoke EnableMenuItem,hMenu,IDM_START_THREAD,MF_GRAYED
invoke EnableMenuItem,hMenu,IDM_STOP_THREAD,MF_ENABLED
.elseif ax==IDM_STOP_THREAD
mov EventStop,TRUE
invoke EnableMenuItem,hMenu,IDM_START_THREAD,MF_ENABLED
invoke EnableMenuItem,hMenu,IDM_STOP_THREAD,MF_GRAYED
.else
invoke DestroyWindow,hWnd
.endif
.endif
.ELSEIF uMsg==WM_FINISH
invoke MessageBox,NULL,ADDR SuccessString,ADDR AppName,MB_OK
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp

ThreadProc PROC USES ecx Param:DWORD


invoke WaitForSingleObject,hEventStart,INFINITE
mov ecx,600000000
.WHILE ecx!=0
.if EventStop!=TRUE
add eax,eax
dec ecx
.else
invoke MessageBox,hwnd,ADDR StopString,ADDR AppName,MB_OK
mov EventStop,FALSE
jmp ThreadProc
.endif
.ENDW
invoke PostMessage,hwnd,WM_FINISH,NULL,NULL
invoke EnableMenuItem,hMenu,IDM_START_THREAD,MF_ENABLED
invoke EnableMenuItem,hMenu,IDM_STOP_THREAD,MF_GRAYED
jmp ThreadProc
ret
ThreadProc ENDP
end start
Analiza:
U ovom primeru demonstriram jos jednu tehniku.
.IF uMsg==WM_CREATE
invoke CreateEvent,NULL,FALSE,FALSE,NULL
mov hEventStart,eax
mov eax,OFFSET ThreadProc
invoke CreateThread,NULL,NULL,eax,\
NULL,0,\
ADDR ThreadID
invoke CloseHandle,eax
Mozete videti da stvaram event objekat i thread bas onda kada program primi WM_CREATE poruku.
Stvaram event objekat u ne signalnom stanju sa automatskim resetovanjem. Nakon sto je event objekat
stvoren, stvaram i thread. Ovaj thread nece odmah krenuti sa izvrsavanjem zato sto ceka na event objekat.
ThreadProc PROC USES ecx Param:DWORD
invoke WaitForSingleObject,hEventStart,INFINITE
mov ecx,600000000
Prva linija procedure threada je poziv ka WaitForSingleObject. Ovim beskonacno dugo cekamo signalno
stanje event objekta pre nego sto cemo nastaviti. Ovo znaci da cak i posto je thread stvoren mi ga na neki
nacin suspendujemo.
Kada korisnik izabere "run thread" komandu iz menija, stavljamo event objekat u signalno stanje.
.if ax==IDM_START_THREAD
invoke SetEvent,hEventStart
Poziv ka SetEvent stavlja event objekat u signalno stanje koje ce redom uciniti da se okonca
WaitForSingleObject funkcija iz procedure threada, a time ce i thread poceti sa radom. Kada korisnik izabere
"stop thread" komandu, smestamo vrednost TRUE u globalnu promenljivu "EventStop".
.if EventStop==FALSE
add eax,eax
dec ecx
.else
invoke MessageBox,hwnd,ADDR StopString,ADDR AppName,MB_OK
mov EventStop,FALSE
jmp ThreadProc

.endif
Ovo zaustavlja thread i skace na poziv ka WaitForSingleObject. Zapazimo da ne moramo rucno da stavimo
event objekat u ne signalno stanje zato sto smo za bManualReset parametar CreteEvent funkcije koristili
FALSE.

Lekcija 17: DLL-ovi


U ovoj lekciji cemo uciti sta je to DLL i kako se pravi. Skinite primer.
Teorija:
Ako se dovoljno dugo bavite programiranjem mozda ste primetili da programi koje pisete cesto imaju neke
iste procedure. Pisanje ovih rutina svaki put iz pocetka je pravo gubljenje vremena. Za vreme DOS-a,
programeri su smestali ove procedure u jednu ili vise biblioteka. Kada bi zeleli da koriste funkcije oni bi
linkovali svoje programe sa bibliotekom pri cemu bi linker ivlacio kod trazene procedure i smestao ga u
finalni izvrsni fajl zajedno sa ostatkom programa. Ovaj proces se naziva staticko vezivanje. Dobar primer
ovoga su biblioteke C-a. Losa strana ove metode je sto imamo identicne procedure u svim programima koji
ih koriste. Na taj nacin se traci memorija. Za DOS programe ovaj metod je prihvatljiv jer uglavnom samo
jedan aktivan program postoji u memoriji tako da se dragocena radna memorija ne trosi na vise kopija
identicnih funkcija.
Pod Windows-om je stanje kriticnije zato sto mozete imati nekoliko programa aktivnih u isto vreme, a
memorija se brzo popuni ako su programi veliki. Resenje ovog problema dolazi u obliku DLL-ova. DLL-ovi
su kolekcije funkcija koje Windows nece ucitavati vise od jednom cak i ako se u datom trenutku izvrsava i
vise kopija vaseg programa. Uvek ce postojati samo jedna kopija DLL-a ucitana u radnu memoriju. U stvari,
svaki aktivni proces koji u datom trenutku koristi neki DLL ce imati svoju kopiju tog DLL-a u memoriji - sam
izvrsni kod DLL-a ce biti isti za sve programe ali ce se DATA sekcija razlikovati od procesa do procesa.
Ovaj program se povezuje sa DLL-om za vreme izvrsavanja sto je drugacije od staticnih biblioteka. Zato se i
zove "Dynamic Link Library". Za vreme izvrsavanja programa vi mozete i da oslobodite DLL kada vam on
vise nije potreban. Ako je to jedini program koji koristi taj DLL on ce biti momentalno izbacen iz radne
memorije ali ako ga koristi jos neki program, DLL ce ostati u memoriji dok god ga ne oslobodi i poslednji
proces koji ga koristi.
Linker ima tezak zadatak kada vezuje DLL sa glavnim programom. Kako ne moze da samo uzme funkcije iz
DLL-a i ubaci ih u finalni izvrsni fajl, on na neki nacin mora da sacuva u izvrsnom fajlu dovoljno informacija
o funkcijama i samom DLL-u kako bi se pravi DLL mogao ucitati za vreme izvrsavanja programa.
Ovde uskacu biblioteke zvane "import libraries". One sadrze informacije o DLL-u koji predstavljaju. Linker
moze da izvuce potrebne stvari iz ovakve biblioteke i da ih ubaci u izvrsni fajl. Kada Windows ucita program
u memoriju, vidi da je on vezan sa nekim DLL-om pa trazi taj DLL, ucitava ga i mapira u memorijski prostor
naseg procesa i zatim izvrsava potrebne korekcije adresa u nasem kodu kako bi svi pozivi ka funkcijama
DLL-a bili pravilno usmereni.
Takodje, mozete izabrati da sami ucitate potrebni DLL sto ima svoje dobre i lose strane.
Nije potrebna "import library" tako da mozete da koristite DLL cak i ako nemate odgovarajucu
biblioteku za njega. Naravno, ipak morate da znate sve o funkcijama tog DLL-a kako bi ga koristili
(broj parametara koje prima svaka funkcija i slicno).
Kada prepustite Windows-u da sam ucita DLL za vas program, a taj DLL ne moze da se nadje,
Windows ce prijaviti da ne moze da pronadje neophodni DLL i tako vas program nece ni dobiti priliku
da se ucita cak i ako DLL bas i nije neophodan za njegovo ucitavanje. Ako sami ucitavate DLL, a ne
mozete da ga nadjete, ukoliko nije neophodan za funkcionisanje programa mozete samo korisniku da
prijavite da DLL nedostaje i da preskocite operaciju za koju vam je on trebao.
Mozete zvati ne dokumentovane funkcije koje se ne nalaze u biblioteci DLL-a, naravno, ukoliko znate
dovoljno o funkciji koju zovete.
Ako koristite LoadLibrary, morate zvati GetProcAddress za svaku funkciju koju zelite da zovete.
GetProcAddress vam daje adresu trazene funkcije u datom DLL-u. Koriscenjem ovoga ce vas kod biti
malo duzi i malo sporiji ali ne puno.
Kako smo videli prednosti i mane LoadLibrary poziva, prelazimo na detalje pravljenja DLL-a.
Kod koji sledi je skelet DLL-a.
;-------------------------------------------------------------------------------------; DLLSkeleton.asm
;-------------------------------------------------------------------------------------.386
.model flat,stdcall
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
.data
.code
DllEntry proc hInstDLL:HINSTANCE, reason:DWORD, reserved1:DWORD
mov eax,TRUE
ret
DllEntry Endp
;--------------------------------------------------------------------------------------------------; This is a dummy function
; It does nothing. I put it here to show where you can insert functions into
; a DLL.
;---------------------------------------------------------------------------------------------------TestFunction proc
ret
TestFunction endp
End DllEntry
;------------------------------------------------------------------------------------; DLLSkeleton.def
;------------------------------------------------------------------------------------LIBRARY DLLSkeleton
EXPORTS TestFunction
Svaki DLL mora imati ulaznu tacku. Ovo je funkcija koju Windows poziva svaki put kada:
Se DLL prvi put ucitava
Odstranjuje iz memorije
Thread (nit egzekucije) je stvorena u istom procesu
Thread (nit egzekucije) je unistena u istom procesu
DllEntry proc hInstDLL:HINSTANCE, reason:DWORD, reserved1:DWORD
mov eax,TRUE
ret
DllEntry Endp
Ova funkcija moze da nosi bilo koje ime sve dok na kraju imate odgovarajuci END <ime funkcije >. Ova
funkcija prima tri parametra od kojih su samo prva dva bitna.
hInstDLL je handle modula. Ovo nije isto sto i "instance handle" procesa. Posto kasnije necete tako lako
doci do ovoga, sacuvajte ga za slucaj da vam zatreba.
reason moze imati jednu od sledece cetiri vrednosti:
DLL_PROCESS_ATTACH DLL prima ovu vrednost kada je po prvi put ubacen u prostor procesa. Ovu
priliku mozete iskoristiti za inicijalizaciju.
DLL_PROCESS_DETACH DLL prima ovu vrednost kada se izbacuje iz prostora procesa. Mozete
iskoristiti ovu priliku za, na primer oslobadjanje alocirane memorije itd.
DLL_THREAD_ATTACH DLL prima ovu vrednost kada proces stvara novi thread (nit egzekucije).
DLL_THREAD_DETACH DLL prima ovu vrednost kada je thread iz procesa unisten.
Vracate TRUE u eax ako zelite da DLL nastavi da radi. Ako vratite FALSE, DLL nece biti ucitan. Na primer,

ako vasa inicijalizacija ukljucuje alociranje neke memorije i to joj ne podje za rukom, ova funkcija moze da
izadje sa FALSE kako bi oznacila da DLL nece raditi.
Svoje funkcije mozete stavljati u DLL posle ili pre ove glavne funkcije (takodje: ulazne funkcije ili tacke). Ali
ako zelite da budu vidljive drugim programima morate staviti njihova imena u eksportnu listu DLL-a (fajl sa
ekstenzijom .def).
Da bi se kompilirao, DLLu je potreban ovaj (.def) fajl. Sada cemo baciti pogled na njega.
LIBRARY DLLSkeleton
EXPORTS TestFunction
Prva linija treba da pocinje sa LIBRARY praceno imenom DLL-a (na primer moje_rutine.dll).
Nakon toga ide EXPORT komanda. Ona govori linkeru koje funkcije treba eksportovati, tj koje ce biti
moguce pozvati iz drugih programa. U primeru, zelimo da drugi moduli imaju mogucnost pozivanja
TestFunction tako da smo njeno ime stavili nakon komande EXPORT.
Druga promena su komande koje trebe dodati komandnoj liniji linkera a to su /DLL i
/DEF:<naziv_vaseg_def_fajla>. Evo primera:
link /DLL /SUBSYSTEM:WINDOWS /DEF:DLLSkeleton.def /LIBPATH:c:\masm32\lib DLLSkeleton.obj
Komandna linija assemblera ostaje ista, /c /coff /Cp. Nakon sto linkujete program dobicete .dll i .lib fajlove.
.lib je biblioteka koju koroistite pri vezivanju ovog DLL-a sa ostalim programima koji ce ga koristiti. Vidite da
je proces isti kao pri vezivanju sa kernel32.dll ili user32.dll.
Sada cu vam pokazati kako da koristite LoadLibrary funkciju kako bi ucitali DLL.
;--------------------------------------------------------------------------------------------; UseDLL.asm
;---------------------------------------------------------------------------------------------.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
.data
LibName db "DLLSkeleton.dll",0
FunctionName db "TestHello",0
DllNotFound db "Cannot load library",0
AppName db "Load Library",0
FunctionNotFound db "TestHello function not found",0
.data?
hLib dd ? ; the handle of the library (DLL)
TestHelloAddr dd ? ; the address of the TestHello function
.code
start:
invoke LoadLibrary,addr LibName
;--------------------------------------------------------------------------------------------------------; Call LoadLibrary with the name of the desired DLL. If the call is successful
; it will return the handle to the library (DLL). If not, it will return NULL
; You can pass the library handle to GetProcAddress or any function that requires
; a library handle as a parameter.
;-----------------------------------------------------------------------------------------------------------.if eax==NULL
invoke MessageBox,NULL,addr DllNotFound,addr AppName,MB_OK
.else
mov hLib,eax

invoke GetProcAddress,hLib,addr FunctionName


;------------------------------------------------------------------------------------------------------------; When you get the library handle, you pass it to GetProcAddress with the address
; of the name of the function in that DLL you want to call. It returns the address
; of the function if successful. Otherwise, it returns NULL
; Addresses of functions don't change unless you unload and reload the library.
; So you can put them in global variables for future use.
;------------------------------------------------------------------------------------------------------------.if eax==NULL
invoke MessageBox,NULL,addr FunctionNotFound,addr AppName,MB_OK
.else
mov TestHelloAddr,eax
call [TestHelloAddr]
;------------------------------------------------------------------------------------------------------------; Next, you can call the function with a simple call with the variable containing
; the address of the function as the operand.
;------------------------------------------------------------------------------------------------------------.endif
invoke FreeLibrary,hLib
;------------------------------------------------------------------------------------------------------------; When you don't need the library anymore, unload it with FreeLibrary.
;------------------------------------------------------------------------------------------------------------.endif
invoke ExitProcess,NULL
end start
Vidite da je koriscenje LoadLibrary za nijansu kompleksnije ali je takodje i malo fleksibilnije.

Lekcija 18: Uobicajene Kontrole (Common Controls)


Naucicemo koje su to uobicajene kontrole i kako se koriste. Ova lekcija ce biti samo kratak uvod. Skinite primer.
Teorija:
Windows 95 dolazi sa nekoliko unapredjenja korisnickog interfejsa, kada se poredi sa starijim izdanjima. Ucinili su ceo
graficki interfejs bogatijim. Neki od tih interfejsa su bili korisceni i pre nego sto se Windows 95 pojavio, kao sto su
statusna linija (statusbar), linija sa alatima (toolbar) i tako dalje. Programeri su sve ovo morali sami da prave. Od doba
Windows 9x i NT ove kontrole se nalaze u paketu sa operativnim sistemom. Ovde cemo govoriti o njima.
Ovo su nove kontrole (nazive sam odlucio da ne prevodim):
Toolbar
Tooltip
Statusbar
Property sheet
Property page
Treeview
Listview
Animation
Draglist
Header
Hot-key
Imagelist
Progressbar
Richedit
Tab
Trackbar
Up-down
Kako ih ima puno, ucitavanje svih i njihovo registrovanje bi bilo tracenje resurasa. Sve ove kontrole, sa izuzetkom
Richedit-a se nalaze u conctl32.dll odakle ih programi mogu ucitati kada ih zele. Ricedit se nalazi u sopstvenom DLL-u richedXX.dll, zato sto je veoma komplikovan i veci od ostalih.
Mozete ucitati comctl32.dll pozivajuci InitCommonControls iz vaseg programa. Ova funkcija se nalazi u comctl32.dll tako da
ce se ovaj DLL ucitati pri ucitavanju vaseg programa ukoliko se ovaj poziv nalazi bilo gde u vasem programu. Ne morate
je cak ni pozivati! Ova funkcija ne radi bas NISTA, sastoji se samo od jedne RET instrukcije! Jedina njena namena je da
ukljuci comctl32.dll u vas program tako da se i on ucita sa vasim programom. Kod koji obavlja sav posao se nalazi u
glavnoj funkciji DLL-a koja se poziva cim se DLL ucita. Tada se registruju sve klase koje se posle koriste za stvaranje
kontrola na isti nacin kao sto se stvaraju polje za unos teksta (edit), liste i druge...
Richedit je prica za sebe. Ako zelite da ga koristite, morate pozvati LoadLibrary da ga eksplicitno ucitate, i da koristite
FreeLibrary da ga oslobodite kada vam vise ne treba.
Sada cemo nauciti kako da ih stvaramo. Mozete koristiti neki editor resurasa kojim cete ih ugraditi u dijalog, ili ih mozete
sami kreirati. Skoro sve uobicajene kontrole se stvaraju CreateWindowEx ili CreateWindow pozivima koristeci odgovarajuce
ime klase. Neke kontrole imaju posebne funkcije za njihovo kreiranje ali se i one u principu oslanjaju na CreateWindowEx.
Postojece specificne funkcije su izlistane:
CreateToolbarEx
CreateStatusWindow
CreatePropertySheetPage
PropertySheet
ImageList_Create
Da bi kreirali uobicajene kontrole morate znati njihova imena. Evo i njih:
Class Name
Common Control
ToolbarWindow32
tooltips_class32
msctls_statusbar32
SysTreeView32
SysListView32
SysAnimate32
SysHeader32
msctls_hotkey32
msctls_progress32

Toolbar
Tooltip
Statusbar
Treeview
Listview
Animation
Header
Hot-key
Progressbar

RICHEDIT
Richedit
msctls_updown32 Up-down
SysTabControl32 Tab
Property sheets, property pages i image list kontrole imaju svoje, specificne funkcije za kreiranje. Drag list kontrole je u
stvari listbox tako da nema sopstvenu klasu. Gore navedena imena klasa su proverena u resurs skriptama koje je stvorio
Visual C++ editor i razlikuju se od naziva koje mozete naci u Borlandovoj Win32 API referenci i u Charles Petzoldovom
"Programming Windows 95". Gore navedena lista je tacna.
Ove uobicajene kontrole mogu da koriste i uobicajene stilove kao sto su na primer WS_CHILD i drugi. Pored ovih imaju i
nesto sopstvenih stilova kao sto su TVS_XXXXX za tree view kontrolu, LVS_XXXXX za list view kontrlu itd. Win32 API
referenca je sto se ovoga tice vas najbolji prijatelj.
Sada kada znamo kako da kreiramo kontrolu mozemo da se pozabavimo sa komunikacijom izmedju kontrola i njihovog
roditelja. Za razliku od ostalih kontrola koje smo videli ranije, ove uobicajene kontrole ne koriste WM_COMMAND za
komunikaciju za roditeljem vec koriste WM_NOTIFY kada se god dogodi nesto interesantno. Roditelj moze da kontrolise
svoju decu slanjem poruka. Postoji puno novih poruka koje odgovaraju ovim kontrolama. Konsultujte Win32 API referencu
za vise informacija.
U narednom primeru cemo ispitati progress bar i status kontrolu.
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\comctl32.inc
includelib \masm32\lib\comctl32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
.const
IDC_PROGRESS equ 1 ; control IDs
IDC_STATUS equ 2
IDC_TIMER equ 3
.data
ClassName db "CommonControlWinClass",0
AppName db "Common Control Demo",0
ProgressClass db "msctls_progress32",0 ; the classname of the progress bar
Message db "Finished!",0
TimerID dd 0
.data?
hInstance HINSTANCE ?
hwndProgress dd ?
hwndStatus dd ?
CurrentStep dd ?
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT
invoke ExitProcess,eax
invoke InitCommonControls
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND

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 hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_APPWORKSPACE
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
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPED+WS_CAPTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
hInst,NULL
mov hwnd,eax
.while TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.endw
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.if uMsg==WM_CREATE
invoke CreateWindowEx,NULL,ADDR ProgressClass,NULL,\
WS_CHILD+WS_VISIBLE,100,\
200,300,20,hWnd,IDC_PROGRESS,\
hInstance,NULL
mov hwndProgress,eax
mov eax,1000 ; the lParam of PBM_SETRANGE message contains the range
mov CurrentStep,eax
shl eax,16 ; the high range is in the high word
invoke SendMessage,hwndProgress,PBM_SETRANGE,0,eax
invoke SendMessage,hwndProgress,PBM_SETSTEP,10,0
invoke CreateStatusWindow,WS_CHILD+WS_VISIBLE,NULL,hWnd,IDC_STATUS
mov hwndStatus,eax
invoke SetTimer,hWnd,IDC_TIMER,100,NULL ; create a timer
mov TimerID,eax
.elseif uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.if TimerID!=0
invoke KillTimer,hWnd,TimerID
.endif
.elseif uMsg==WM_TIMER ; when a timer event occurs
invoke SendMessage,hwndProgress,PBM_STEPIT,0,0 ; step up the progress in the progress bar
sub CurrentStep,10
.if CurrentStep==0
invoke KillTimer,hWnd,TimerID
mov TimerID,0
invoke SendMessage,hwndStatus,SB_SETTEXT,0,addr Message

invoke MessageBox,hWnd,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION


invoke SendMessage,hwndStatus,SB_SETTEXT,0,0
invoke SendMessage,hwndProgress,PBM_SETPOS,0,0
.endif
.else
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.endif
xor eax,eax
ret
WndProc endp
end start
Analiza:
invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT
invoke ExitProcess,eax
invoke InitCommonControls
Namerno sam stavio InitCommonControls posle ExitProcess poziva kako bi demonstrirao da InitCommonControls postoji
samo kako bi se u import sekciju ukljucio i comctl32.dll. Kao sto vidite, uobicajene kontrole rade i ako se ova funkcija
nikada ne pokrene.
.if uMsg==WM_CREATE
invoke CreateWindowEx,NULL,ADDR ProgressClass,NULL,\
WS_CHILD+WS_VISIBLE,100,\
200,300,20,hWnd,IDC_PROGRESS,\
hInstance,NULL
mov hwndProgress,eax
Ovde stvaramo uobicajenu kontrolu. Primetite da je u ovom CreateWindowEx pozivu hWnd u stvari handle roditelja kao i
da takodje dodeljuje ID kontroli. Ipak kako imamo handle kontrole ovaj ID nam je nepotreban. Kako sva deca tako i ova
kontrola mora imati WS_CHILD stil.
mov eax,1000
mov CurrentStep,eax
shl eax,16
invoke SendMessage,hwndProgress,PBM_SETRANGE,0,eax
invoke SendMessage,hwndProgress,PBM_SETSTEP,10,0
Nakon sto je progress bar stvoren treba da podesimo ovir u kome ce se kretati. Predefinisana vrednost je od 0 do 100.
Ako vam se to ne dopada mozete sami podesiti ove vrednosti preko PBM_SETRANGE poruke. lParam ove poruke je okvir
gde je u prvih 16 bita gornja a u drugih 16 bita donja granica. Preko PBM_SETSTEP poruke mozete podesiti broj jedinica
koje ce predstavljati jedan korak. U primeru je ovo 10 sto znaci da kada posaljete PBM_STEPIT poruku, indikator ce se
pomeriti za 10 jedinica. Sa PBM_SETPOS porukom mozete po voli podesiti trenutnu vrednost indikatora. Ova poruka vam
daje bolju kontrolu nad progress bar-om.
invoke CreateStatusWindow,WS_CHILD+WS_VISIBLE,NULL,hWnd,IDC_STATUS
mov hwndStatus,eax
invoke SetTimer,hWnd,IDC_TIMER,100,NULL ; create a timer
mov TimerID,eax
Sada cemo da napravimo status bar pozivajuci CreateStatusWindow. Ovaj poziv je lak za razumevanje tako da ga necu
komentarisati. Nakon sto je status stvoren, stvaramo timer koji ce osvezavati progres u intervalu od 100ms. Evo prototipa
funkcije SetTimer.
SetTimer PROTO hWnd:DWORD, TimerID:DWORD, TimeInterval:DWORD, lpTimerProc:DWORD
1. hWnd handle prozora - roditelja.
2. TimerID proizvoljan ne nula broj koji ce biti identifikator timer.
3. TimerInterval interval timer-a u milisekundama koje moraju proci pre nego sto ce timer pozvati svoju proceduru ili
poslati WM_TIMER poruku.
4. lpTimerProc Adresa funkcije koju ce timer pozvati kada zadati interval prodje. Ako je ovo NULL, timer ce poslati
WM_TIMER poruku prozoru roditelju. Ako je poziv uspesan vratice se identifikacija timer-a, u suprotnom 0 (nula) i
zato identifikacija ne sme biti nula.
.elseif uMsg==WM_TIMER
invoke SendMessage,hwndProgress,PBM_STEPIT,0,0
sub CurrentStep,10
.if CurrentStep==0
invoke KillTimer,hWnd,TimerID
mov TimerID,0
invoke SendMessage,hwndStatus,SB_SETTEXT,0,addr Message

invoke MessageBox,hWnd,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION


invoke SendMessage,hwndStatus,SB_SETTEXT,0,0
invoke SendMessage,hwndProgress,PBM_SETPOS,0,0
.endif
Kada odrednjeni interval istekne timer salje WM_TIMER PORUKU. Tu cete smestiti kod koji zelite da se izvrsi. U primeru
osvezimo progress bar i zatim proverimo da li je maksimalna vrednost dostignuta. Ako jeste gasimo timer i SB_SETTEXT
porukom stavljamo tekst u status kontrolu. Prikazuje se poruka i nakon sto korisnik stisne OK brisemo tekst iz statusa i
resetujemo progress.

file:///D|/Download%20Free%20zona/Knjige%20Racunari%20staro/Win32asm/tut018.html[18.4.2010 3:07:59]

Вам также может понравиться