Download the example here.
CreateThread
proto lpThreadAttributes:DWORD,\
dwStackSize:DWORD,\
lpStartAddress:DWORD,\
lpParameter:DWORD,\
dwCreationFlags:DWORD,\
lpThreadId:DWORD
CreateThread
function looks a lot like CreateProcess.
lpThreadAttributes
--> You can use NULL if you want the thread to have default security descriptor.
dwStackSize
--> specify the stack size of the thread.
If you want the thread to have the same stack size as the primary thread,
use NULL as this parameter.
lpStartAddress-->
Address of the thread function.It's the function that will perform the
work of the thread. This function MUST receive one and only one 32-bit
parameter and return a 32-bit value.
lpParameter
--> The parameter you want to pass to the thread function.
dwCreationFlags
--> 0 means the thread runs immediately after it's created. The opposite
is CREATE_SUSPENDED flag.
lpThreadId
--> CreateThread function will fill the thread
ID of the newly created thread at this address.
If
CreateThread call is sucessful, it returns the handle of the newly created
thread. Otherwise, it returns NULL.
The
thread function runs as soon as CreateThread call is success ful unless
you specify CREATE_SUSPENDED flag in dwCreationFlags. In that case, the
thread is suspended until ResumeThread function is called.
When
the thread function returns with ret instruction, Windows calls ExitThread
function for the thread function implicitly. You can call ExitThread function
with in your thread function yourself but there' s little point in doing
so.
You
can retrieve the exit code of a thread by calling GetExitCodeThread function.
If
you want to terminate a thread from other thread, you can call TerminateThread
function. But you should use this function under extreme circumstance since
this function terminates the thread immediately without giving the thread
any chance to clean up after itself.
Now
let's move to the communication methods between threads.
There
are three of them:
WM_MYCUSTOMMSG equ WM_USER+100h
Windows
will not use any value from WM_USER upward for its own messages so you
can use the value WM_USER and above as your own custom message value.
If
one of the thread is a user interface thread and the other is a worker
one, you cannot use this method as two-way communication since a worker
thread doesn't have its own window so it doesn't have a message queue.
You can use the following scheme:
User interface Thread ------> global variable(s)----> Worker thread
Worker Thread ------> custom window message(s) ----> User interface
Thread
In
fact, we will use this method in our example.
The
last communication method is an event object. You can view an event object
as a kind of flag. If the event object is in "unsignalled" state, the thread
is dormant or sleeping, in this state, the thread doesn't receive CPU time
slice. When the event object is in "signalled" state,Windows "wakes up"
the thread and it starts performing the assigned task.
.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
.if ax==IDM_CREATE_THREAD
mov eax,OFFSET ThreadProc
invoke CreateThread,NULL,NULL,eax,\
NULL,0,\
ADDR ThreadID
invoke CloseHandle,eax
The
above function creates a thread that will run a procedure named ThreadProc
concurrently with the primary thread. After the successful call, CreateThread
returns immediately and ThreadProc begins to run. Since we do not use thread
handle, we should close it else there'll be some leakage of memory. Note
that closing the thread handle doesn't terminate the thread. Its only effect
is that we cannot use the thread handle anymore.
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
As you can see, ThreadProc performs a savage calculation which takes quite a while to finish and when it finishs it posts a WM_FINISH message to the main window. WM_FINISH is our custom message defined like this: