Download the example here.
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
// address of thread security attributes
DWORD dwStackSize,
// initial thread stack size, in bytes
LPTHREAD_START_ROUTINE lpStartAddress,
// address of thread function
LPVOID lpParameter,
// argument for new thread
DWORD dwCreationFlags,
// creation flags
LPDWORD lpThreadId
// address of returned thread identifier
);
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
--> The same as those in CreateProcess function.
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.
include windows.inc
includelib user32.lib
includelib kernel32.lib
includelib gdi32.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
invoke WinMain, hInstance,NULL,CommandLine,
SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:SDWORD
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 hInstance
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,0
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
mov eax,uMsg
.IF eax==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF eax==WM_COMMAND
mov eax,wParam
.if lParam==0
.if ax==IDM_CREATE_THREAD
mov eax,OFFSET ThreadProc
invoke CreateThread,NULL,NULL,eax,\
NULL,NORMAL_PRIORITY_CLASS,\
ADDR ThreadID
invoke CloseHandle,eax
.else
invoke DestroyWindow,hWnd
.endif
.endif
.ELSEIF eax==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,NORMAL_PRIORITY_CLASS,\
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.
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: