Powrót do strony wyboru lekcji

Programowanie wykorzystujące
wielozadaniowość systemu 2



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.

Przykład

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


Nie można wyświetlić obrazu
Rys.1. Interfejs programu przykładowego podczas wyświetlania tekstu
generowanego przez 2 wątki.


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

Pobierz kod

Ćwiczenie 8

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.

Pobierz szablon

Nie można wyświetlić obrazu
Rys.2. Interfejs programu.

<Poprzednia lekcjaKolejna lekcja>