第十六课 事件对象
CreateEvent proto lpEventAttributes:DWORD,\ 
                              
bManualReset:DWORD,\ 
                              
bInitialState:DWORD,\ 
                              
lpName:DWORD 
lpEventAttribute--> 
如果是NULL值,产生的事件对象有缺省的安全属性。
bManualReset--> 
如果想在每次调用WaitForSingleObject 
后让WINDOWS为您自动地把事件地状态恢复为”无信号”状态,必须把该参数设为FALSE,否则,您必须每次调用ResetEvent函数来清除事件的信号。
bInitialState--> 
刚刚产生事件对象时的状态。如果设为TRUE是”有信号”,否则是”无信号”。
lpName --> 
事件对象的名称。您在OpenEvent函数中可能使用。 
如果CreateEvent调用成功的话,会返回新生成的对象的句柄,否则返回NULL。
这里有两个API函数用来修改事件对象的信号状态:SetEvent和ResetEvent。前者把事件对象设为”有信号”状态,而后者正好相反。
在事件对象生成后,必须调用WaitForSingleObject来让线程进入等待状态,该函数的语法如下: 
WaitForSingleObject proto hObject:DWORD, dwTimeout:DWORD
hObject -->指向同步对象的指针。事件对象其实是同步对象的一种。
dwTimeout --> 
等待同步对象变成”有信号”前等待的时间,以毫秒计。当等待的时间超过该值后无信号同步对象仍处于”无信号”状态,线程不再等待,WaitForSingleObject函数会返回。如果想要线程一直等待,请把该参数设为INFINITE(该值等于0xffffffff)。 
.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_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 
    .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 
在WM_CREATE 消息的处理中我们生成事件同步对象并创建线程。我们设置了相关的值让同步对象生成时处于”无信号”状态而且在调用了WaitForSingleObject后可以自动把事件对象的状态设为”无信号”。然后我们创建线程。 线程的代码开始执行后立即被阻塞:
ThreadProc PROC USES ecx Param:DWORD 
        invoke 
WaitForSingleObject,hEventStart,INFINITE 
        mov  ecx,600000000 
您可以看到线程的执行体的第一条代码就是调用WaitForSingleObject函数,该函数使得线程阻塞并且一直处于等待事件对象变成”有信号”。这也就是说,我们以开始就让该线程进入了睡眠状态。 当用户选择了菜单项”run thread”后,我们把事件对象得状态变成”有信号”:
            .if 
ax==IDM_START_THREAD 
                
invoke SetEvent,hEventStart 
函数SetEvent可以让同步对象变成”有信号”状态,那么下一次线程得到时间片运行时,WaitForSingleObject函数就会返回,线程余下的代码就可以得到执行了。当用户选择了菜单项”stop thread” 时,我们把全局变量EventStop设为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 
这样线程得计数工作结束,然后跳转到重新执行WaitForSingleObject函数的地方。注意:我们不用手动清除事件对象的信号,因为在调用CreateEvent函数时把参数bManualReset的值设为了FALSE。