| ||
Now, it is time to consider threads in more detail. First, try to solve the problem from the previous chapter (see Listing 14.2) using the thread.
A thread can be created using the CreateThread function. This function accepts the following parameters:
First parameterThis is a pointer to the structure of the access attributes. It has meaning only for the Windows NT family. As a rule, this parameter is set to NULL .
Second parameterThis is the size of the thread's stack. If this parameter is set to zero, then the default stack size is taken, which is the size of the stack of the parent thread.
Third parameterThis is a pointer to the thread function. Thread execution starts from the call to this function.
Fourth parameterThis is the parameter for the thread function.
Fifth parameterThis flag defines the thread state. If this flag is zero, then thread execution starts immediately. If the flag value is CREATE_SUSPENDED (4H) , the thread is in the waiting state and starts after the ResumeThread function has been executed.
Sixth parameterThis is the pointer to the variable, into which the thread handle will be placed.
As already mentioned, thread execution starts from the thread function. Termination of this function naturally terminates the thread (exiting the function using RET ). The thread can also terminate execution by executing the ExitThread function, specifying the exit code. Finally, the generating thread might terminate the operation of the generated thread using the TerminateThread function. In Listing 15.2, the generated process cannot terminate its operation and terminates with the application in response to TerminateThread . It is necessary to point out that such termination is abnormal and is not recommended for everyday use. This is because when using this type of termination, the system doesn't carry out any actions related to releasing allocated resources (memory blocks, opened files, etc.). Therefore, it is recommended that you build your applications so that the thread would terminate by exiting the thread procedure. Thus, the example in Listing 15.2 demonstrates some of don'ts.
In an ideal situation, the window function takes responsibility for reaction only to events that take place in relation to its elements; all other jobs (e.g., complex computations and file processing) are the responsibility of threads. By the way, a thread can generate other threads, so an entire branched tree may appear as a result.
As I have mentioned already, the program in Listing 15.2 uses a thread to compute the current data and time and display them in the edit window. Note that if such processing is implemented in the window function, you would immediately notice the difference because the window would cease to react to nearly all of your actions.
// The TIMER2.RC file // Constants definitions #define WS_SYSMENU Ox00080000L // The window elements must be initially visible #define WS_VISIBLE 0xl0000000L // Border around the element #define WS_BORDER 0x00800000L // Elements can be activated using the <Tab> key #define WS_TABSTOP 0x00010000L // Text in the edit field is left-aligned #define ES_LEFT 0x0000L // Style of all window elements #define WS_CHILD 0x40000000L // Keyboard input is disabled #define ES_READONLY 0x0800L // Button style #define BS_PUSHBUTTON 0x00000000L // Center the button label #define BS_CENTER 0x00000300L #define DS_3DLOOK 0x0004L // Dialog box definition DIAL1 DIALOG 0, 0, 240, 100 STYLE WS_SYSMENU DS_3DLOOK CAPTION "Example of the thread usage" FONT 8, "Arial" { // Edit field, identifier 1 CONTROL "", 1, "edit", ES_LEFT WS_CHILD WS_VISIBLE WS_BORDER WS_TABSTOP ES_READONLY, 100, 5, 130, 12 // Button, identifier 2 CONTROL "Exit", 2, "button", BS_PUSHBUTTON BS CENTER WS_CHILD WS_VISIBLE WS_TABSTOP, 180, 76, 50, 14 } ; The TIMER2.INC file ; Constants ; The message arrives when the window is closed WM_CLOSE equ 10h ; The message arrives when the window is created WM_INITDIALOG equ 110h ; The message arrives when something happens to ; the window elements WM_COMMAND equ 111h ; This message sends the text to the window element WM_SETTEXT equ 0Ch ; Prototypes of external procedures IFDEF MASM ; For MASM EXTERN SendMessageA@16:NEAR EXTERN GetDlgItem@8:NEAR EXTERN Sleep@4:NEAR EXTERN TerminateThread@8:NEAR EXTERN CreateThread@24:NEAR EXTERN wsprintfA:NEAR EXTERN GetLocalTime@4:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetModuleHandleA@ 4:NEAR EXTERN DialogBoxParamA@20:NEAR EXTERN EndDialog@8:NEAR ELSE ; For TASM EXTERN SendMessageA:NEAR EXTERN GetDlgItem:NEAR EXTERN Sleep:NEAR EXTERN TerminateThread:NEAR EXTERN CreateThread:NEAR EXTERN _wsprintfA:NEAR EXTERN GetLocalTime:NEAR EXTERN ExitProcess:NEAR EXTERN GetModuleHandleA:NEAR EXTERN DialogBoxParamA:NEAR EXTERN EndDialog:NEAR SendMessageA@16 = SendMessageA GetDlgItem@8 = GetDlgItem Sleep@4 = Sleep TerminateThread@8 = TerminateThread CreateThread@24 = CreateThread wsprintfA = _wsprintfA GetLocalTime@4 = GetLocalTime ExitProcess@4 = ExitProcess GetModuleHandleA@4 = GetModuleHandleA DialogBoxParamA@20 = DialogBoxParamA EndDialog@8 = EndDialog ENDIF ; Structures ; Message structure MSGSTRUCT STRUC MSHWND DD ? MSMESSAGE DD ? MSWPARAM DD ? MSLPARAM DD ? MSTIME DD ? MSPT DD ? MSGSTRUCT ENDS ; Date-time data structure DAT STRUC year DW ? month DW ? dayweek DW ? day DW ? hour DW ? min DW ? sec DW ? msec DW ? DAT ENDS ; The TIMER2.ASM file .586P ; Flat memory model .MODEL FLAT, stdcall include thread.inc ; INCLUDELIB directives for the linker to link libraries IFDEF MASM ; For the LINK.EXE linker includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib includelib c:\masm32\lib\gdi32.lib ELSE ; For the TLINK32.EXE linker includelib c:\tasm32\lib\import32.lib ENDIF ;----------------------------------------------- ; Data segment _DATA SEGMENT MSG MSGSTRUCT <?> HINST DD 0 ; Application descriptor PA DB "DIAL1", 0 TIM DB "Date %u/%u/%u Time %u:%u:%u", 0 STRCOPY DB 50 DUP(?) DATA DAT <0> HTHR DD ? _DATA ENDS ; Code segment _TEXT SEGMENT START: ; Get the application descriptor PUSH 0 CALL GetModuleHandleA@4 MOV [HINST], EAX ; Create a dialog PUSH 0 PUSH OFFSET WNDPROC PUSH 0 PUSH OFFSET PA PUSH [HINST] CALL DialogBoxParamA@20 CMP EAX, -1 JNE KOL ; Error message KOL: ;--------------------------------- PUSH 0 CALL ExitProcess@4 ;--------------------------------- ; Window procedure ; Position of parameters in the stack ; [EBP+014H] ; LPARAM ; [EBP+10H] ; WAPARAM ; [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_CLOSE JNE L1 L3: ; The reaction to closing the window ; Kill the thread PUSH 0 PUSH HTHR CALL TerminateThread@8 ; Close the dialog PUSH 0 PUSH DWORD PTR [EBP+08H] CALL EndDialog@8 JMP FINISH L1: CMP DWORD PTR [EBP+0CH], WM_INITDIALOG JNE L2 ; Startup initialization ; Get the descriptor of the edit field PUSH 1 PUSH DWORD PTR [EBP+08H] CALL GetDlgItem@8 ; Create a thread PUSH OFFSET HTHR ; Thread descriptor goes here PUSH 0 PUSH EAX ; Parameter PUSH OFFSET GETTIME ; Procedure address PUSH 0 PUSH 0 CALL CreateThread@24 JMP FINISH L2: CMP DWORD PTR [EBP+0CH], WM_COMMAND JNE FINISH ; Exit button? CMP WORD PTR [EBP+10H], 2 JE L3 FINISH: POP EDI POP ESI POP EBX POP EBP MOV EAX, 0 RET 16 WNDPROC ENDP ; Thread function ; [EBP+8] Parameter = Edit field descriptor GETTIME PROC PUSH EBP MOV EBP, ESP LO: ; 1-second delay PUSH 1000 CALL Sleep@4 ; Get local time PUSH OFFSET DATA CALL GetLocalTime@4 ; Get the string for data and time output MOVZX EAX, DATA.sec PUSH EAX MOVZX EAX, DATA.min PUSH EAX MOVZX EAX, DATA.hour PUSH EAX MOVZX EAX, DATA.year PUSH EAX MOVZX EAX, DATA. month PUSH EAX MOVZX EAX, DATA.day PUSH EAX PUSH OFFSET TIM PUSH OFFSET STRCOPY CALL wsprintfA ; Send the string to the edit window PUSH OFFSET STRCOPY PUSH 0 PUSH WM_SETTEXT PUSH DWORD PTR [EBP+08H] CALL SendMessageA@16 JMP LO ; Endless loop POP EBP RET 4 GETTIME ENDP _TEXT ENDS END START
To translate the THREAD.ASM program presented in Listing 15.2, issue the following commands using MASM32:
ML /c /coff /DMASM thread.asm! RC thread.rc LINK /SUBSYSTEM:WINDOWS thread.obj thread.res
Issue the following commands using TASM32:
TASM32 /ml thread.asm BRCC32 thread.rc TLINK32 -aa thread.obj,,,,,thread.res
I recommend that you pay attention to a useful function Sleep . This function is frequently used in threads because it frees some processor time.
| ||