前言
我对Windows线程API有一种恐怖的感觉,那是因为在以前第一次接触它的时候是通过VB,好吧,就是VB,在他的开发环境中做子类化或者多线程编程时,IDE总会莫名其妙经常崩溃,以至于后面继续是哪怕改了一个字符,运行时都要先保存一下,因为这个IDE可没有自动保存功能。
但说回来,并不是API恐怖,而是IDE恐怖。
CreateThread
见名知意,这是创建线程的函数,如果函数成功,则返回新线程的句柄。
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
__drv_aliasesMem LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
参数介绍:
- lpThreadAttributes指向SECURITY_ATTRIBUTES 结构,表示线程内核对象的安全属性,一般传入NULL就行,表示使用默认设置。
- dwStackSize线程栈空间大小。传入0表示使用默认大小。
- lpStartAddress线程执行的函数地址,就是线程跑的代码段在哪个函数中。
- lpParameter线程函数的参数
- dwCreationFlags这个参数为0表示线程创建后马上就可以进行调度,如果为CREATE_SUSPENDED则表示线程创建后暂停运行,直到调用ResumeThread()。
- lpThreadId用于获取返回的线程的ID,传入NULL表示不需要返回该线程ID号。
还有两个创建线程的函数是_beginthread()或_beginthreadex(),自行了解一下。
代码示例
我们知道在做一个耗时较长的任务时,需要开启新线程,否则会阻塞后面代码执行,引起界面未响应。
下面简单做一个示例,开启一个线程,这个线程每隔1秒打印一次,一共打印5次。
.386
.model flat,stdcall
option casemap:none
include c:\masm32\include\windows.inc
include c:\masm32\include\user32.inc
includelib c:\masm32\lib\user32.lib
include c:\masm32\include\kernel32.inc
includelib c:\masm32\lib\kernel32.lib
include c:\masm32\include\msvcrt.inc
includelib c:\masm32\lib\msvcrt.lib
WinMain proto :DWORD
.DATA
ClassName db "MyWindowClass",0
AppName db "Win32线程",0
ButtonClassName db "Button",0
ButtonTitle db "开启线程",0
HelloGUI db "HelloGUI",0
Message db "完成",0
MessageTitle db "提示",0
OutMessage db "第%d次"
.DATA?
hInstance HINSTANCE ?
hWindowHdc HDC ?
hButton HWND ?
hWindow HWND ?
ThreadID DWORD ?
.CONST
ButtonID equ 1
.CODE
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke WinMain, hInstance
invoke ExitProcess, eax
WinMain proc hInst:HINSTANCE
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,NULL
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,NULL,\
ADDR ClassName,\
ADDR AppName,\
WS_OVERLAPPEDWINDOW,\
0,\
0,\
300,\
300,\
NULL,\
NULL,\
hInst,\
NULL
mov hWindow,eax
invoke CreateWindowEx,NULL,ADDR ButtonClassName,ADDR ButtonTitle, WS_TABSTOP OR WS_VISIBLE OR WS_CHILD OR BS_DEFPUSHBUTTON ,\
0,0,100,100,hWindow,ButtonID,NULL,NULL
mov hButton,eax
invoke GetDC,eax
mov hWindowHdc,eax
invoke ShowWindow, hWindow,SW_SHOWDEFAULT
invoke UpdateWindow, hWindow
.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
PrintThread proc num:dword
invoke crt_printf, addr OutMessage,num
.while num>0
sub num,1
invoke Sleep,1000
invoke crt_printf, addr OutMessage,num
.endw
invoke MessageBox,NULL,addr Message,addr MessageTitle,MB_OK
ret
PrintThread endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL strRect:RECT
.IF uMsg == WM_LBUTTONDOWN
mov strRect.right,100
mov strRect.bottom,100
mov strRect.left,0
mov strRect.top,0
invoke DrawText,hWindowHdc, ADDR HelloGUI,8,ADDR strRect,DT_VCENTER
.ELSEIF uMsg ==WM_COMMAND
mov eax,wParam
.IF ax == ButtonID
mov eax,OFFSET PrintThread
invoke CreateThread,NULL,NULL,eax,5,0,ADDR ThreadID
invoke CloseHandle,eax
.ENDIF
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
ret
WndProc endp
end start
如果在主线程开启循环打印的话,窗口就会无法拖动,但如果在新线程执行的话,窗口就可以拖动了。