W ramach jednego procesu programu mogą być tworzone tzw. Wątki. Wątek w odróżnieniu od procesu dzieli z innymi wątkami w ramach procesu wspólną przestrzeń adresową. Procesy natomiast posiadają oddzielne przestrzenie - nie mają dostępu do danych innych procesów. Operacja przełączania pomiędzy wątkami realizowana jest znacznie szybciej, niż przełączenia pomiędzy procesami wykonującymi się w tym samym czasie. Jednak możliwość korzystania ze wspólnych danych przez wątki w ramach jednego procesu, może prowadzić do utraty spójności i w rezultacie błędnego działania programu. Dlatego też programista piszący aplikacje wielowątkowe powinien odpowiednio zabezpieczyć sekcje programu, w których wątki próbują modyfikować jednocześnie te same dane.
Tworzymy program wyświetlający napis - "Kolorowy napis". Napis rysowany jest przez 2 wątki równocześnie lub jeden wątek, w zależności od wyboru użytkownika. Pierwszy wątek rysuje litery napisu w kolorze czerwonym a drugi w zielonym. Aby zabezpieczyć przed jednoczesnym dostępem do liter napisu zastosowano tzw. sekcję krytyczną. Chroni ona dany fragmentu kodu, zapewniając iż będzie on wykonywany tylko przez jeden wątek naraz. Pozostałe wątki są wstrzymywane, do momentu opuszczenia sekcji przez aktualny wątek. Dopiero po zakończeniu sekcji przez aktualnie wykonywany wątek, możliwe jest wykonanie sekcji przez kolejny wątek w kolejce. Na początku programu definiujemy typ instrukcji procesora, określamy model pamięci jako płaski z wywołaniem procedur typu STDCALL. Dołączamy także odpowiednie biblioteki: user32.lib , kernel32.lib oraz dodatkowo bibliotekę gdi32.lib:
.586 ;typ instrukcji procesora
.MODEL
FLAT,STDCALL
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\gdi32.lib
Następnie definiujemy pomocne makro to tworzenia kontrolek w oknie przy pomocy funkcji CreateWindowExA@48:
KONTROLKA MACRO Par1,Par2,Par3,Par4,Par5,Par6,Par7,Par8,Par9,Par10,Par11,Par12
PUSH Par1
PUSH Par2
PUSH Par3
PUSH Par4
PUSH Par5
PUSH Par6
PUSH Par7
PUSH Par8
PUSH Par9
PUSH Par10
PUSH Par11
PUSH Par12
CALL CreateWindowExA@48
ENDM
Stałe jakie wykorzystamy w programie:
WM_COMMAND EQU 111h
WM_DESTROY EQU 2
WM_CREATE EQU 1
BS_DEFPUSHBUTTON EQU 1h
WS_VISIBLE EQU 10000000h
WS_CHILD EQU 40000000h
WS_SYSMENU EQU 80000h
WS_MANIMIZEBOX EQU 20000h
WS_OVERLAPPEDWINDOW EQU WS_SYSMENU + WS_MANIMIZEBOX
CS_VREDRAW EQU 1h
CS_HREDRAW EQU 2h
CS_GLOBALCLASS EQU 4000h
STYLE EQU CS_HREDRAW OR CS_VREDRAW OR CS_GLOBALCLASS
STYLBTN EQU WS_CHILD + BS_DEFPUSHBUTTON + WS_VISIBLE
IDI_APPLICATION EQU 32512
IDC_CROSS EQU 32512
SW_SHOWNORMAL EQU 1
RED EQU 233
GREEN EQU 233
BLUE EQU 233
RED1 EQU 0
GREEN1 EQU 224
BLUE1 EQU 0
RGBW EQU (RED or (GREEN shl 8) or (BLUE shl 16))
RGBT EQU 255
RGBT1 EQU (RED1 or (GREEN1 shl 8) or (BLUE1 shl 16))
RED ,GREEN i BLUE oraz RED1 ,GREEN1 i BLUE1 to odpowiednio stałe definiujące wartości składowe (czerwona, zielona i niebieska) formatu RGB . Następnie składowe te wykorzystywane są w stałych RGBW i RGBT1 poprzez połączenie ich operatorem or. Pozostała lista stałych w programie była już opisywana we wcześniejszych lekcjach. Lista funkcji API, które zostały użyte w programie wygląda następująco:
EXTERN TerminateThread@8:NEAR
EXTERN Sleep@4:NEAR
EXTERN CreateThread@24:NEAR
EXTERN InitializeCriticalSection@4:NEAR
EXTERN EnterCriticalSection@4:NEAR
EXTERN LeaveCriticalSection@4:NEAR
EXTERN DeleteCriticalSection@4:NEAR
EXTERN GetTextExtentPoint32A@16:NEAR
EXTERN MessageBoxA@16:NEAR
EXTERN CreateWindowExA@48:NEAR
EXTERN DefWindowProcA@16:NEAR
EXTERN DispatchMessageA@4:NEAR
EXTERN ExitProcess@4:NEAR
EXTERN GetMessageA@16:NEAR
EXTERN GetModuleHandleA@4:NEAR
EXTERN LoadCursorA@8:NEAR
EXTERN LoadIconA@8:NEAR
EXTERN PostQuitMessage@4:NEAR
EXTERN RegisterClassA@4:NEAR
EXTERN ShowWindow@8:NEAR
EXTERN TranslateMessage@4:NEAR
EXTERN UpdateWindow@4:NEAR
EXTERN TextOutA@20:NEAR
EXTERN SetBkColor@8:NEAR
EXTERN SetTextColor@8:NEAR
EXTERN GetDC@4:NEAR
EXTERN DeleteDC@4:NEAR
Funkcja TerminateThread@8 to wymuszenie zakończenia wątku o podanym w pierwszym parametrze uchwycie. Funkcja Sleep@4 wymusza uśpienie wątku na podany w parametrze czas w milisekundach. Funkcja CreateThread@24 tworzy nowy wątek. Postać tej funkcji w API przedstawiona jest na poniższym listingu:
HANDLE WINAPI CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
Pierwszy parametr - lpThreadAttributes, to wskaźnik na strukturę atrybutów dostępu. Z reguły nadawana jest mu wartość 0. Drugi parametr - dwStackSize, określa rozmiar stosu wątku. Jeżeli zostanie mu nadana wartość zero, to przyjmowany jest domyślny rozmiar stosu, czyli rozmiar stosu procesu-przodka. LpStartAddress to wskaźnik na funkcję wątku. LpParameter to parametr przekazywany do funkcji wątku. Piąty parametr - dwCreationFlags, to znacznik określający stan wątku. Jeśli ma on wartość zero to wątek jest uruchamiany natychmiast. Jeśli jednk, zostanie mu podana wartość 4h, to wątek tworzony jest w stanie uśpionym i uruchamiany dopiero po wywołaniu funkcji ResumeThread. Szósty parametr - lpThreadId, to uchwyt wątku.
Po funkcji CreateThread@24 zdeklarowano 4 funkcje obsługi sekcji krytycznej. Funkcja InitializeCriticalSection@4 inicjuje funkcję krytyczną, EnterCriticalSection@4 wchodzi do sekcji krytycznej., LeaveCriticalSection@4 opuszcza sekcję krytyczną, a DeleteCriticalSection@4 niszczy sekcję krytyczną. Funkcja TextOutA@20 wypisuje w określonej lokalizacji tekst o podanych w parametrach właściwościach. SetBkColor@8 ustawia tło koloru, na którym wyświetlany jest tekst. SetTextColor@8 ustawia kolor wyświetlanego tekstu. GetDC@4 pobiera uchwyt kontekstu urządzenia wyświetlanego tekstu. Analogicznie funkcja DeleteDC@4 kasuje dany kontekst urządzenia.
Następnie definiujemy potrzebne struktury do użycia w programie:
MSGSTRUCT STRUC
MSHWND DD ? ;uchwyt komunikatu
MSMESSAGE DD ? ;nazwa komunikatu
MSWPARAM DD ? ;dodatkowa informacja komunikatu
MSWLARAM DD ? ;dodatkowa informacja komunikatu
MSTIME DD ? ;określa czas w który wiadomość była wysłana
MSPT DD ? ;określa pozycję kursora
MSGSTRUCT ENDS
WNDCLASS STRUC
CLSSTYLE DD ? ;styl okna
CLWNDPROC DD ? ;adres procedury okna
CLSCBCLSEX DD ? ;dodatkowe bajty na miejsce dla struktury w pamięci
CLSCBWNDEX DD ? ;dodatkowe bajty na miejsce dla egzemplarza w pamięci
CLSHINST DD ? ;uchwyt danego egzemplarza modułu
CLSHICON DD ? ;uchwyt ikony z funkcji LoadIcon
CLSHCURSOR DD ? ;uchwyt kursora z funkcji LoadCursor
CLBKGROUND DD ? ;kolor tła okna
CLMENUNAME DD ? ;uchwyt menu
CLNAME DD ? ;nazwa klasy okna
WNDCLASS ENDS
CRIT STRUC
DD ?
DD ?
DD ?
DD ?
DD ?
DD ?
CRIT ENDS
SIZET STRUC
X1 DD ?
Y1 DD ?
SIZET ENDS
Struktura CRIT to struktura wykorzystywana przez funkcje operujące na sekcji krytycznej, przekazywana im jako parametr. Pola tej struktury z punktu widzenia programistów są nieistotne, gdyż są one wykorzystywane wewnętrznie przez funkcje operujące na sekcjach krytycznych. Struktura SIZET określa długość tekstu.
Sekcja danych niezainicjalizowanych naszego programu wygląda następująco:
.DATA?
SK CRIT <?>
MSG MSGSTRUCT <?>
WC WNDCLASS <?>
SZT SIZET <?>
HW DD ? ;uchwyt okna zdefiniowanego za pomocą funkcji CreateWindowExA
DC DD ? ;kontekst urządzenia
THR1 DD ? ;uchwyt wątku nr. 1
THR2 DD ? ;uchwyt wątku nr. 2
Sekcja danych zainicjalizowanych programu wygląda następująco:
.DATA
HWNDBTN1 DWORD 0 ;uchwyt przycisku nr 1
HWNDBTN2 DWORD 0 ;uchwyt przycisku nr 2
CLSBUTN DB 'BUTTON', 0
CPBUT2 DB '2 wątki', 0
CPBUT1 DB '1 wątek', 0
HINST DD 0
NEWHWND DD 0
TITLENAME DB 'Generacja napisu przez wątki', 0
NAM DB 'CLASS32', 0
XT DWORD 30
YT DWORD 30
KONCZ1 DB 0
KONCZ2 DB 0
TEXT DB 'Kolorowy tekst', 0
PUSTY DB ' ', 0
IND DD 0
FLAG3 DB 0
Zmienne KONCZ1 i KONCZ2 posłużą jako flagi informujące o zakończeniu odpowiednio pierwszego bądź drugiego wątku. Zmienna TEXT przechowuje tekst, który będzie generowany przez 2 utworzone nowe wątki. PUSTY to ciąg spacji, służący wykasowaniu narysowanego prędzej tekstu. IND przechowuje aktualną literę do wyświetlania przez któryś z dwóch wątków. Aby dwa wątki nie próbowały wpisać do zmiennej przechowującą aktualną literę w tym samym czasie, będzie ona uzupełniana w sekcji krytycznej. Zmienna FLAG3 również posłuży jako flaga informująca o tym czy sekcja krytyczna programu została już zainicjalizowana.
Po deklaracji wszystkich danych użytych w programie, przechodzimy do sekcji kodu. Początek sekcji wygląda następująco:
.CODE
START:
PUSH 0
CALL GetModuleHandleA@4
MOV [HINST],EAX
MOV [WC.CLSSTYLE],STYLE
MOV [WC.CLWNDPROC],OFFSET WNDPROC
MOV [WC.CLSCBCLSEX],0
MOV [WC.CLSCBWNDEX],0
MOV EAX,[HINST]
MOV [WC.CLSHINST],EAX
PUSH IDI_APPLICATION
PUSH 0
CALL LoadIconA@8
MOV [WC.CLSHICON],EAX
PUSH IDC_CROSS
PUSH 0
CALL LoadCursorA@8
MOV [WC.CLSHCURSOR],EAX
MOV [WC.CLBKGROUND],16
MOV DWORD PTR [WC.CLMENUNAME],0
MOV DWORD PTR [WC.CLNAME],OFFSET NAM
PUSH OFFSET WC
CALL RegisterClassA@4
PUSH 0
PUSH [HINST]
PUSH 0
PUSH 0
PUSH 400
PUSH 400
PUSH 100
PUSH 100
PUSH WS_OVERLAPPEDWINDOW
PUSH OFFSET TITLENAME
PUSH OFFSET NAM
PUSH 0
CALL CreateWindowExA@48
CMP EAX,0
JZ _ERR
MOV [NEWHWND],EAX
PUSH SW_SHOWNORMAL
PUSH [NEWHWND]
CALL ShowWindow@8
PUSH [NEWHWND]
CALL UpdateWindow@4
MSG_LOOP:
PUSH 0
PUSH 0
PUSH 0
PUSH OFFSET MSG;wskaźnik na strukturę komunikatu
CALL GetMessageA@16
CMP AX,0;czy koniec?
JE END_LOOP
PUSH OFFSET MSG
CALL TranslateMessage@4 ;tłumaczenie
PUSH OFFSET MSG
CALL DispatchMessageA@4 ;wysłanie
JMP MSG_LOOP
END_LOOP:
PUSH [MSG.MSWPARAM]
CALL ExitProcess@4
_ERR:
JMP
END_LOOP
;parametry:
;[EBP +14H] LPARAM
;[EBP +10H] WPARAM
;[EBP +0CH] MES
;[EBP +8] HWND
WNDPROC PROC
PUSH EBP
MOV EBP,ESP
PUSH EBX
PUSH ESI
PUSH EDI
CMP DWORD PTR [EBP + 0CH],WM_DESTROY;komunikat związany ze zniszczeniem okna
JE WMDESTROY
CMP DWORD PTR [EBP + 0CH],WM_CREATE;tworzenie kontrolek w oknie
JE WMCREATE
CMP DWORD PTR [EBP + 0CH],WM_COMMAND;zdarzenie związane z kontrolką
JE WMCOMMAND
JMP DEFWNDPROC;nieobsłużone komunikaty kierujemy do DefWindowProcA@16
WMCOMMAND:
W sekcji WMCOMMAND ładujemy do flag KONCZ1 i KONCZ2 jedynki, informując tym samym iż zakończyliśmy wykonywanie prędzej uruchomionych wątków funkcjami TerminateThread@8. Następnie pobieramy kontekst urządzenia i przygotowujemy okno do wyświetlenia napisu. Ustawiamy na początku zmienną PUSTY, jako wyświetlany tekst w oknie oraz inicjalizujemy sekcje krytyczną:
MOV KONCZ1,1
MOV KONCZ2,1
PUSH 0
PUSH THR1
CALL TerminateThread@8
PUSH 0
PUSH THR2
CALL TerminateThread@8
;pobranie kontekstu urządzenia dla okna o uchwycie HW
PUSH HW
CALL GetDC@4
MOV DC,EAX
;ustawienie koloru tła
PUSH RGBW
PUSH EAX
CALL SetBkColor@8
;ustawienie tekstu w określonej lokalizacji okna i rysowanie tekstu
PUSH 30
PUSH OFFSET PUSTY
PUSH YT
PUSH 130
PUSH DC
CALL TextOutA@20
PUSH OFFSET SZT
PUSH 30
PUSH OFFSET PUSTY
PUSH DC
CALL GetTextExtentPoint32A@16
CMP FLAG3,0
JNE DALEJ
MOV FLAG3,1
PUSH OFFSET SK
CALL InitializeCriticalSection@4
MOV EAX,0
DALEJ:
Następnie sprawdzamy czy któryś z przycisków nie został wybrany przez użytkownika i w zależności od wyboru tworzymy jeden lub dwa wątki funkcją CreateThread@24, którymi będziemy rysować nasz tekst:
MOV EAX,HWNDBTN2
CMP DWORD PTR [EBP + 14H],EAX ;Czy LPARAM to uchwyt przycisku?
JNE PRZYCISK2
LEA EAX,TEXT
MOV IND,EAX
MOV XT,130
PUSH OFFSET THR1
PUSH 0
PUSH EAX
PUSH OFFSET THREAD1
PUSH 0
PUSH 0
CALL CreateThread@24
PUSH OFFSET THR2
PUSH 0
PUSH EAX
PUSH OFFSET THREAD2
PUSH 0
PUSH 0
CALL CreateThread@24
JMP FINISH
PRZYCISK2:
MOV EAX,HWNDBTN1
CMP DWORD PTR [EBP + 14H],EAX ;Czy LPARAM to uchwyt przycisku?
JNE OMIN
LEA EAX,TEXT
MOV IND,EAX
MOV XT,130
PUSH OFFSET THR1
PUSH 0
PUSH EAX
PUSH OFFSET THREAD1
PUSH 0
PUSH 0
CALL CreateThread@24
JMP FINISH
OMIN:
Zmienna IND przyjmuje w chwili uruchomienia jednego bądź dwóch wątków - adres pierwszego znaku w zmiennej tekstowej TEXT. Współrzędna X wyświetlanego tekstu ustawiana jest na 130. W sekcji WMCREATE tworzymy kontrolki przycisków, przy pomocy wcześniej zdefiniowanego makra. Kopiujemy także uchwyt naszego okna do zmiennej HW. W sekcji WMDESTROY dodatkowo wywołujemy funkcję niszczącą sekcję krytyczną- DeleteCriticalSection@4. Pozostała część kodu funkcji WNDPROC nie różni się niczym, biorąc pod uwagę jej definicję w poprzednich lekcjach:
WMCREATE:
MOV EAX,DWORD PTR [EBP + 08H]
MOV HW,EAX
KONTROLKA 0,[HINST],0,HW,40,100,100 ,140,STYLBTN , OFFSET CPBUT1, OFFSET CLSBUTN,0
MOV HWNDBTN1, EAX
XOR EAX,EAX
KONTROLKA 0,[HINST],0,HW,40,100,160 ,140,STYLBTN , OFFSET CPBUT2, OFFSET CLSBUTN,0
MOV HWNDBTN2, EAX
XOR EAX,EAX
JMP FINISH
DEFWNDPROC:
PUSH DWORD PTR [EBP + 14H]
PUSH DWORD PTR [EBP + 10H]
PUSH DWORD PTR [EBP + 0CH]
PUSH DWORD PTR [EBP + 08H]
CALL DefWindowProcA@16
JMP FINISH
WMDESTROY:
PUSH OFFSET SK
CALL DeleteCriticalSection@4
PUSH 0
CALL PostQuitMessage@4
XOR EAX,EAX
FINISH:
POP EDI
POP ESI
POP EBX
POP EBP
RET
WNDPROC ENDP
Następnie definiujemy procedury obsługi wątków, które będziemy tworzyć w programie. Zaczynamy od procedury pierwszego wątku rysującej litery napisu w kolorze czerwonym:
THREAD1 PROC
MOV KONCZ1, 0
LO1:
MOV EBX,IND
CMP BYTE PTR [EBX],0
JE _END1
Na początku ładujemy do flagi KONCZ1 - 0. Następnie sprawdzamy czy pod adresem w zmiennej IND nie znajduje się już koniec napisu - znak 0. Jeśli tak, skaczemy na koniec procedury wątku i kończymy wątek, w przeciwnym wypadku wchodzimy do sekcji krytycznej i wyświetlamy aktualną literę pod adresem w zmiennej IND w kolorze czerwonym. Zwiększamy współrzędną x i adres następnej litery. Następnie funkcją LeaveCriticalSection@4 opuszczamy sekcję krytyczną aktualnym wątkiem, dając możliwość wejścia do sekcji kolejnemu oczekującemu wątkowi:
PUSH OFFSET SK
CALL EnterCriticalSection@4
PUSH HW
CALL GetDC@4
MOV DC,EAX
;ustawienie koloru tła
PUSH RGBW
PUSH EAX
CALL SetBkColor@8
;ustawienie koloru tekstu
PUSH RGBT
PUSH DC
CALL SetTextColor@8
;ustawienie tekstu w określonej lokalizacji okna i rysowanie tekstu
PUSH 1
PUSH IND
PUSH YT
PUSH XT
PUSH DC
CALL TextOutA@20
PUSH OFFSET SZT
PUSH 1
PUSH IND
PUSH DC
CALL GetTextExtentPoint32A@16
MOV EAX,SZT.X1
ADD XT, EAX
INC IND
;kasujemy kontekst urządzenia
PUSH DC
CALL DeleteDC@4
PUSH OFFSET SK
CALL LeaveCriticalSection@4
Po opuszczeniu sekcji usypiamy wątek na jedną sekundę i sprawdzamy czy przypadkiem flaga KONCZ1 nie jest jedynką. Jeśli tak, kończymy wykonywanie wątku, w przeciwnym wypadku wykonujemy skok do początku pętli:
PUSH 1000
CALL Sleep@4
CMP KONCZ1,1
JE _END1
JMP LO1
_END1:
RET
THREAD1 ENDP
Procedura obsługi drugiego wątku pod względem funkcjonowania wygląda identycznie jak pierwszego. Jedynymi różnicami są nazwy poszczególnych etykiet i kolor wyświetlanego tekstu:
THREAD2 PROC
MOV KONCZ2, 0
LO2:
MOV EBX,IND
CMP BYTE PTR [EBX],0
JE _END2
PUSH OFFSET SK
CALL EnterCriticalSection@4
PUSH HW
CALL GetDC@4
MOV DC,EAX
;ustawienie koloru tła
PUSH RGBW
PUSH EAX
CALL SetBkColor@8
;ustawienie koloru tekstu
PUSH RGBT1
PUSH DC
CALL SetTextColor@8
;ustawienie tekstu w określonej lokalizacji okna i rysowanie tekstu
PUSH 1
PUSH IND
PUSH YT
PUSH XT
PUSH DC
CALL TextOutA@20
PUSH OFFSET SZT
PUSH 1
PUSH IND
PUSH DC
CALL GetTextExtentPoint32A@16
MOV EAX,SZT.X1
ADD XT, EAX
INC IND
;kasujemy kontekst urządzenia
PUSH DC
CALL DeleteDC@4
PUSH OFFSET SK
CALL LeaveCriticalSection@4
PUSH 1000
CALL Sleep@4
CMP KONCZ2,1
JE _END2
JMP LO2
_END2:
RET
THREAD2 ENDP
END Start
Przykładowe wywołanie programu przestawione jest na rysunku poniżej - Rys.1
Całość programu przedstawiona jest na poniższym listingu:
.586 ;typ instrukcji procesora
.MODEL
FLAT,STDCALL
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\gdi32.lib
KONTROLKA MACRO Par1,Par2,Par3,Par4,Par5,Par6,Par7,Par8,Par9,Par10,Par11,Par12
PUSH Par1
PUSH Par2
PUSH Par3
PUSH Par4
PUSH Par5
PUSH Par6
PUSH Par7
PUSH Par8
PUSH Par9
PUSH Par10
PUSH Par11
PUSH Par12
CALL CreateWindowExA@48
ENDM
WM_COMMAND EQU 111h
WM_DESTROY EQU 2
WM_CREATE EQU 1
BS_DEFPUSHBUTTON EQU 1h
WS_VISIBLE EQU 10000000h
WS_CHILD EQU 40000000h
WS_SYSMENU EQU 80000h
WS_MANIMIZEBOX EQU 20000h
WS_OVERLAPPEDWINDOW EQU WS_SYSMENU + WS_MANIMIZEBOX
CS_VREDRAW EQU 1h
CS_HREDRAW EQU 2h
CS_GLOBALCLASS EQU 4000h
STYLE EQU CS_HREDRAW OR CS_VREDRAW OR CS_GLOBALCLASS
STYLBTN EQU WS_CHILD + BS_DEFPUSHBUTTON + WS_VISIBLE
IDI_APPLICATION EQU 32512
IDC_CROSS EQU 32512
SW_SHOWNORMAL EQU 1
RED EQU 233
GREEN EQU 233
BLUE EQU 233
RED1 EQU 0
GREEN1 EQU 224
BLUE1 EQU 0
RGBW EQU (RED or (GREEN shl 8) or (BLUE shl 16))
RGBT EQU 255
RGBT1 EQU (RED1 or (GREEN1 shl 8) or (BLUE1 shl 16))
EXTERN TerminateThread@8:NEAR
EXTERN Sleep@4:NEAR
EXTERN CreateThread@24:NEAR
EXTERN InitializeCriticalSection@4:NEAR
EXTERN EnterCriticalSection@4:NEAR
EXTERN LeaveCriticalSection@4:NEAR
EXTERN DeleteCriticalSection@4:NEAR
EXTERN GetTextExtentPoint32A@16:NEAR
EXTERN MessageBoxA@16:NEAR
EXTERN CreateWindowExA@48:NEAR
EXTERN DefWindowProcA@16:NEAR
EXTERN DispatchMessageA@4:NEAR
EXTERN ExitProcess@4:NEAR
EXTERN GetMessageA@16:NEAR
EXTERN GetModuleHandleA@4:NEAR
EXTERN LoadCursorA@8:NEAR
EXTERN LoadIconA@8:NEAR
EXTERN PostQuitMessage@4:NEAR
EXTERN RegisterClassA@4:NEAR
EXTERN ShowWindow@8:NEAR
EXTERN TranslateMessage@4:NEAR
EXTERN UpdateWindow@4:NEAR
EXTERN TextOutA@20:NEAR
EXTERN SetBkColor@8:NEAR
EXTERN SetTextColor@8:NEAR
EXTERN GetDC@4:NEAR
EXTERN DeleteDC@4:NEAR
MSGSTRUCT STRUC
MSHWND DD ? ;uchwyt komunikatu
MSMESSAGE DD ? ;nazwa komunikatu
MSWPARAM DD ? ;dodatkowa informacja komunikatu
MSWLARAM DD ? ;dodatkowa informacja komunikatu
MSTIME DD ? ;określa czas w który wiadomość była wysłana
MSPT DD ? ;określa pozycję kursora
MSGSTRUCT ENDS
WNDCLASS STRUC
CLSSTYLE DD ? ;styl okna
CLWNDPROC DD ? ;adres procedury okna
CLSCBCLSEX DD ? ;dodatkowe bajty na miejsce dla struktury w pamięci
CLSCBWNDEX DD ? ;dodatkowe bajty na miejsce dla egzemplarza w pamięci
CLSHINST DD ? ;uchwyt danego egzemplarza modułu
CLSHICON DD ? ;uchwyt ikony z funkcji LoadIcon
CLSHCURSOR DD ? ;uchwyt kursora z funkcji LoadCursor
CLBKGROUND DD ? ;kolor tła okna
CLMENUNAME DD ? ;uchwyt menu
CLNAME DD ? ;nazwa klasy okna
WNDCLASS ENDS
CRIT STRUC
DD ?
DD ?
DD ?
DD ?
DD ?
DD ?
CRIT ENDS
SIZET STRUC
X1 DD ?
Y1 DD ?
SIZET ENDS
.DATA?
SK CRIT <?>
MSG MSGSTRUCT <?>
WC WNDCLASS <?>
SZT SIZET <?>
HW DD ? ;uchwyt okna zdefiniowanego za pomocą funkcji CreateWindowExA
DC DD ? ;kontekst urządzenia
THR1 DD ? ;uchwyt wątku nr. 1
THR2 DD ? ;uchwyt wątku nr. 2
.DATA
HWNDBTN1 DWORD 0 ;uchwyt przycisku nr 1
HWNDBTN2 DWORD 0 ;uchwyt przycisku nr 2
CLSBUTN DB 'BUTTON', 0
CPBUT2 DB '2 wątki', 0
CPBUT1 DB '1 wątek', 0
HINST DD 0
NEWHWND DD 0
TITLENAME DB 'Generacja napisu przez wątki', 0
NAM DB 'CLASS32', 0
XT DWORD 30
YT DWORD 30
KONCZ1 DB 0
KONCZ2 DB 0
TEXT DB 'Kolorowy tekst', 0
PUSTY DB ' ', 0
IND DD 0
FLAG3 DB 0
.CODE
START:
PUSH 0
CALL GetModuleHandleA@4
MOV [HINST],EAX
MOV [WC.CLSSTYLE],STYLE
MOV [WC.CLWNDPROC],OFFSET WNDPROC
MOV [WC.CLSCBCLSEX],0
MOV [WC.CLSCBWNDEX],0
MOV EAX,[HINST]
MOV [WC.CLSHINST],EAX
PUSH IDI_APPLICATION
PUSH 0
CALL LoadIconA@8
MOV [WC.CLSHICON],EAX
PUSH IDC_CROSS
PUSH 0
CALL LoadCursorA@8
MOV [WC.CLSHCURSOR],EAX
MOV [WC.CLBKGROUND],16
MOV DWORD PTR [WC.CLMENUNAME],0
MOV DWORD PTR [WC.CLNAME],OFFSET NAM
PUSH OFFSET WC
CALL RegisterClassA@4
PUSH 0
PUSH [HINST]
PUSH 0
PUSH 0
PUSH 400
PUSH 400
PUSH 100
PUSH 100
PUSH WS_OVERLAPPEDWINDOW
PUSH OFFSET TITLENAME
PUSH OFFSET NAM
PUSH 0
CALL CreateWindowExA@48
CMP EAX,0
JZ _ERR
MOV [NEWHWND],EAX
PUSH SW_SHOWNORMAL
PUSH [NEWHWND]
CALL ShowWindow@8
PUSH [NEWHWND]
CALL UpdateWindow@4
MSG_LOOP:
PUSH 0
PUSH 0
PUSH 0
PUSH OFFSET MSG;wskaźnik na strukturę komunikatu
CALL GetMessageA@16
CMP AX,0;czy koniec?
JE END_LOOP
PUSH OFFSET MSG
CALL TranslateMessage@4 ;tłumaczenie
PUSH OFFSET MSG
CALL DispatchMessageA@4 ;wysłanie
JMP MSG_LOOP
END_LOOP:
PUSH [MSG.MSWPARAM]
CALL ExitProcess@4
_ERR:
JMP
END_LOOP
;parametry:
;[EBP +14H] LPARAM
;[EBP +10H] WPARAM
;[EBP +0CH] MES
;[EBP +8] HWND
WNDPROC PROC
PUSH EBP
MOV EBP,ESP
PUSH EBX
PUSH ESI
PUSH EDI
CMP DWORD PTR [EBP + 0CH],WM_DESTROY;komunikat związany ze zniszczeniem okna
JE WMDESTROY
CMP DWORD PTR [EBP + 0CH],WM_CREATE;tworzenie kontrolek w oknie
JE WMCREATE
CMP DWORD PTR [EBP + 0CH],WM_COMMAND;zdarzenie związane z kontrolką
JE WMCOMMAND
JMP DEFWNDPROC;nieobsłużone komunikaty kierujemy do DefWindowProcA@16
WMCOMMAND:
MOV KONCZ1,1
MOV KONCZ2,1
PUSH 0
PUSH THR1
CALL TerminateThread@8
PUSH 0
PUSH THR2
CALL TerminateThread@8
;pobranie kontekstu urządzenia dla okna o uchwycie HW
PUSH HW
CALL GetDC@4
MOV DC,EAX
;ustawienie koloru tła
PUSH RGBW
PUSH EAX
CALL SetBkColor@8
;ustawienie tekstu w określonej lokalizacji okna i rysowanie tekstu
PUSH 30
PUSH OFFSET PUSTY
PUSH YT
PUSH 130
PUSH DC
CALL TextOutA@20
PUSH OFFSET SZT
PUSH 30
PUSH OFFSET PUSTY
PUSH DC
CALL GetTextExtentPoint32A@16
CMP FLAG3,0
JNE DALEJ
MOV FLAG3,1
PUSH OFFSET SK
CALL InitializeCriticalSection@4
MOV EAX,0
DALEJ:
MOV EAX,HWNDBTN2
CMP DWORD PTR [EBP + 14H],EAX ;Czy LPARAM to uchwyt przycisku?
JNE PRZYCISK2
LEA EAX,TEXT
MOV IND,EAX
MOV XT,130
PUSH OFFSET THR1
PUSH 0
PUSH EAX
PUSH OFFSET THREAD1
PUSH 0
PUSH 0
CALL CreateThread@24
PUSH OFFSET THR2
PUSH 0
PUSH EAX
PUSH OFFSET THREAD2
PUSH 0
PUSH 0
CALL CreateThread@24
JMP FINISH
PRZYCISK2:
MOV EAX,HWNDBTN1
CMP DWORD PTR [EBP + 14H],EAX ;Czy LPARAM to uchwyt przycisku?
JNE OMIN
LEA EAX,TEXT
MOV IND,EAX
MOV XT,130
PUSH OFFSET THR1
PUSH 0
PUSH EAX
PUSH OFFSET THREAD1
PUSH 0
PUSH 0
CALL CreateThread@24
JMP FINISH
OMIN:
WMCREATE:
MOV EAX,DWORD PTR [EBP + 08H]
MOV HW,EAX
KONTROLKA 0,[HINST],0,HW,40,100,100 ,140,STYLBTN , OFFSET CPBUT1, OFFSET CLSBUTN,0
MOV HWNDBTN1, EAX
XOR EAX,EAX
KONTROLKA 0,[HINST],0,HW,40,100,160 ,140,STYLBTN , OFFSET CPBUT2, OFFSET CLSBUTN,0
MOV HWNDBTN2, EAX
XOR EAX,EAX
JMP FINISH
DEFWNDPROC:
PUSH DWORD PTR [EBP + 14H]
PUSH DWORD PTR [EBP + 10H]
PUSH DWORD PTR [EBP + 0CH]
PUSH DWORD PTR [EBP + 08H]
CALL DefWindowProcA@16
JMP FINISH
WMDESTROY:
PUSH OFFSET SK
CALL DeleteCriticalSection@4
PUSH 0
CALL PostQuitMessage@4
XOR EAX,EAX
FINISH:
POP EDI
POP ESI
POP EBX
POP EBP
RET
WNDPROC ENDP
THREAD1 PROC
MOV KONCZ1, 0
LO1:
MOV EBX,IND
CMP BYTE PTR [EBX],0
JE _END1
PUSH OFFSET SK
CALL EnterCriticalSection@4
PUSH HW
CALL GetDC@4
MOV DC,EAX
;ustawienie koloru tła
PUSH RGBW
PUSH EAX
CALL SetBkColor@8
;ustawienie koloru tekstu
PUSH RGBT
PUSH DC
CALL SetTextColor@8
;ustawienie tekstu w określonej lokalizacji okna i rysowanie tekstu
PUSH 1
PUSH IND
PUSH YT
PUSH XT
PUSH DC
CALL TextOutA@20
PUSH OFFSET SZT
PUSH 1
PUSH IND
PUSH DC
CALL GetTextExtentPoint32A@16
MOV EAX,SZT.X1
ADD XT, EAX
INC IND
;kasujemy kontekst urządzenia
PUSH DC
CALL DeleteDC@4
PUSH OFFSET SK
CALL LeaveCriticalSection@4
PUSH 1000
CALL Sleep@4
CMP KONCZ1,1
JE _END1
JMP LO1
_END1:
RET
THREAD1 ENDP
THREAD2 PROC
MOV KONCZ2, 0
LO2:
MOV EBX,IND
CMP BYTE PTR [EBX],0
JE _END2
PUSH OFFSET SK
CALL EnterCriticalSection@4
PUSH HW
CALL GetDC@4
MOV DC,EAX
;ustawienie koloru tła
PUSH RGBW
PUSH EAX
CALL SetBkColor@8
;ustawienie koloru tekstu
PUSH RGBT1
PUSH DC
CALL SetTextColor@8
;ustawienie tekstu w określonej lokalizacji okna i rysowanie tekstu
PUSH 1
PUSH IND
PUSH YT
PUSH XT
PUSH DC
CALL TextOutA@20
PUSH OFFSET SZT
PUSH 1
PUSH IND
PUSH DC
CALL GetTextExtentPoint32A@16
MOV EAX,SZT.X1
ADD XT, EAX
INC IND
;kasujemy kontekst urządzenia
PUSH DC
CALL DeleteDC@4
PUSH OFFSET SK
CALL LeaveCriticalSection@4
PUSH 1000
CALL Sleep@4
CMP KONCZ2,1
JE _END2
JMP LO2
_END2:
RET
THREAD2 ENDP
END Start
Zmodyfikować kod przykład tak, aby można było generować napis trzema różnymi wątkami. Trzeci wątek powinien rysować litery napisu w kolorze niebieskim. Przykładowe wywołanie takiego programu przedstawiono na rysunku poniżej - Rys.2.