In this lesson, we'll learn about event objects and how to use them in multithreaded programming.
Theory:
In the previous lesson, we demonstrated how to communicate with Windows messages between different threads. The other two, that is, using global variables and event objects, will be explained in this lesson.
The event object is like a switch: it has only two states---open and close. When an event is in the "open" state, we call it "signal" or "no signal". You can create an event object in the execution function of a thread, and then observe its state, and if it is "no signal," Let the thread sleep, so that the thread consumes less CPU time.
The function that produces the event object is as follows:
CreateEvent Proto Lpeventattributes:dword,\
Bmanualreset:dword,\
Binitialstate:dword,\
Lpname:dword
Lpeventattribute--> if it is a null value, the resulting event object has the default security attribute.
Bmanualreset--> You must set this argument to False if you want Windows to automatically restore the state of the event to a "no signal" state after each call to WaitForSingleObject. You must call the ResetEvent function every time to clear the signal of the event.
Binitialstate--> the state of the event object when it was just generated. If set to true is "signal", otherwise "no signal".
The name of the Lpname--> event object. You may use it in the OpenEvent function.
If the CreateEvent call succeeds, the handle of the newly generated object is returned, or null is returned.
Here are two API functions to modify the signal state of an event object: SetEvent and ResetEvent. The former sets the event object to a "signal" state, while the latter is the opposite.
After the event object is generated, you must call WaitForSingleObject to get the thread into the waiting state, which has the following syntax:
WaitForSingleObject Proto Hobject:dword, Dwtimeout:dword
Hobject--> A pointer to the synchronization object. Event objects are actually one of the synchronized objects.
dwTimeOut the time, in milliseconds, before the--> waits for the synchronized object to become "signaled." When the wait time exceeds this value, no signal synchronization object is still in the "no signal" state, the thread is no longer waiting, and the WaitForSingleObject function returns. If you want the thread to wait all the time, set the argument to infinite (the value is equal to 0xFFFFFFFF).
Example:
The following example shows a window where the thread begins a simple count operation when the user selects the menu item "Run Thread". A dialog box is displayed to notify the user after the end. During the entire count, you can select the menu item "Stop thread" to terminate the thread at any time.
.386
. Model Flat,stdcall
Option Casemap:none
WinMain Proto:D Word,:D Word,:D Word,:D Word
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
Analysis:
In this example, we demonstrate another technique:
. 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
In the processing of WM_CREATE messages we generate event synchronization objects and create threads. We set the associated values so that the synchronization object is generated in a "no signal" state and can automatically set the state of the event object to "no signal" after the WaitForSingleObject is invoked. Then we create the thread. The thread's code is blocked immediately after it starts executing:
ThreadProc PROC USES ecx Param:dword
Invoke Waitforsingleobject,heventstart,infinite
MOV ecx,600000000
The first piece of code that you can see the thread's execution body is the call to the WaitForSingleObject function, which causes the thread to block and is always waiting for the event object to become "signaled." That is to say, we start by getting the thread into a sleep state. When the user selects the menu item "Run Thread", we change the event object status to "signaled":
. If Ax==idm_start_thread
Invoke Setevent,heventstart
Function SetEvent allows the synchronization object to become a "signaled" state, so the next time the thread gets the time slice run, the WaitForSingleObject function returns, and the remaining code for the thread is executed. When the user selects the menu item "Stop Thread", we set the global variable Eventstop to TRUE.
. If Eventstop==false
Add Eax,eax
Dec ecx
. else
Invoke Messagebox,hwnd,addr stopstring,addr APPNAME,MB_OK
MOV Eventstop,false
JMP ThreadProc
. endif
This way the thread has to count the end of the work and then jump to the place where the WaitForSingleObject function is executed again. Note: We do not have to manually clear the signal of the event object because the value of the parameter bManualReset is set to False when the CreateEvent function is invoked.