Zoom Icon

Tutorial 16: Event Object

From UIC

Tutorial 16: Event Object

Contents


Tutorial 16: Event Object
Author: Iczelion
Email: Translate: -NeuRaL_NoiSE
Website: Mirror
Date: 17/01/2009 (dd/mm/yyyy)
Level: Luck and skills are required
Language: Italian Flag Italian.gif
Comments: Formattazione Wiki: Antelox


Introduzione

Impareremo cos'è un event object e come usarlo in un programma in multithreading.


Tools

Esempio


Preliminari

Nel tutorial precedente, ho dimostrato come i thread comunicano tra di loro con un messaggio di windows personalizzato. Non ho parlato di altri due metodi: le variabili globali e gli event objects. Li utilizzeremo entrambi in questo tutorial. Un event object e' come un interruttore: ha solo due stati, ON o OFF. Quando un event object e' accesso (ON), e' in stato "segnalato". Quando e' spento (OFF), e' instato "non-segnalato". Voi create un event object e inserite uno spezzone di codice nei thread interessati per controllare lo stato dell'event object. Se l'event object e' in stato "non-segnalato", i threads che lo aspettano saranno "addormentati". Quando i threads sono in stato di attesa, consumano poco CPU time. Creerete un event object chiamando la funzione CreateEvent che ha la seguente sintassi:

HANDLE CreateEvent(
    LPSECURITY_ATTRIBUTES  lpEventAttributes, // indirizzo degli attributi di sicurezza
    BOOL  bManualReset,                  // flag per l'evento di reset manuale
    BOOL  bInitialState,                 // flag per lo stato iniziale
    LPCTSTR  lpName                      // indirizzo del nome dell'event object
   );

lpEventAttribute --> Se specificate il valore NULL, l'event object e' creato con il descrittore di sicurezza predefinito.

bManualReset --> Se volete che Windows resetti automaticamente l'event object in stato "non-segnalato" dopo la chiamata a WaitForSingleObject, dovete specificare FALSE in questo parametro. Altrimenti, dovete resettare manualmente l'event object con la chiamata a ResetEvent.

bInitialState --> Se volete che l'event object venga creato in stato "segnalato", specificate TRUE come parametro o altrimenti l'event object sara' creato in stato "non-segnalato".

lpName --> Puntatore ad una stringa ASCIIZ che contiene il nome dell'event object. Questo nome e' usato quando volete chiamare OpenEvent.

Se la chiamata ha successo, restituisce l'handle del nuovo event object, altrimenti restituisce NULL. Potete modificare lo stato di un event object con due funzioni API: SetEvent e ResetEvent. La funzione SetEvent mette l'event object in stato "segnalato". ResetEvent fa il contrario. Quando un event object viene creato, dovete inserire la chiamata a WaitForSingleObject nel thread che deve controllare lo stato dell'event object. WaitForSingleObject ha la seguente sintassi:

DWORD WaitForSingleObject(
    HANDLE  hObject,         // handle dell'object da aspettare
    DWORD  dwTimeout      // intervallo di time-out in millisecondi
   );

hObject --> L'handle di uno dei synchronization objects. L'event object e' un tipo di synchronization object.

dwTimeout --> Specifica il tempo in millisecondi che questa funzione aspettera' per vedere se l'object e' in stato "segnalato". Se il tempo specificato e' passato e l'event object e' ancora in stato "non-segnalato", WaitForSingleObject ritorna al caller. Se volete aspettare l'object per un tempo indefinito, dovete specificare il valore INFINITE in questo parametro.


Essay

L'esempio che segue mostra una finestra che aspetta che l'utente selezioni un comando dal menu. Se l'utente sceglie "run thread", il thread comincia la savage calculation. Quando ha finito, viene mostrata una message box che informa l'utente che il lavoro e' stato completato. Mentre il thread e' in esecuzione, l'utente puo' scegliere "stop thread" per fermarlo.

include windows.inc
includelib user32.lib
includelib kernel32.lib
includelib gdi32.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

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hwnd HANDLE ?
hMenu HANDLE ?
ThreadID DWORD ?
ExitCode DWORD ?
hEventStart HANDLE ?
EventStop BOOL FALSE

.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
    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
    mov   eax,uMsg
    .IF eax==WM_CREATE
        invoke CreateEvent,NULL,FALSE,FALSE,NULL
        mov  hEventStart,eax
        mov  eax,OFFSET ThreadProc
        invoke CreateThread,NULL,NULL,eax,\
                             NULL,NORMAL_PRIORITY_CLASS,\
                             ADDR ThreadID
        invoke CloseHandle,eax
    .ELSEIF eax==WM_DESTROY
        invoke PostQuitMessage,NULL
    .ELSEIF eax==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 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
        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


Analisi

In quest'esempio, dimostro un altra tecnica di thread.

.IF eax==WM_CREATE
        invoke CreateEvent,NULL,FALSE,FALSE,NULL
        mov  hEventStart,eax
        mov  eax,OFFSET ThreadProc
        invoke CreateThread,NULL,NULL,eax,\
                             NULL,NORMAL_PRIORITY_CLASS,\
                             ADDR ThreadID
        invoke CloseHandle,eax

Potete vedere che creo l'event object e il thread durante l'elaborazione del messaggio WM_CREATE. Creo l'event object in stato "non-segnalato" con reset automatico. Una volta creato l'event object, creo il thread. Comunque il thread non parte immediatamente perche' attende che l'event object sia in stato "segnalato" con il codice seguente:

ThreadProc PROC USES ecx Param:DWORD
        invoke WaitForSingleObject,hEventStart,INFINITE
        mov  ecx,600000000

La prima linea della thread procedure e' la chiamata a WaitForSingleObject. Essa aspetta indefinitamente per lo stato "segnalato" dell'event object prima di ritornare. Cio' significa che nonostante noi creiamo il thread, lo mettiamo in stato di inattivita'. Quando l'utente sceglie il comando "run thread" dal menu, mettiamo l'event object in stato "segnalato" cosi':

.if ax==IDM_START_THREAD
                invoke SetEvent,hEventStart

La chiamata a SetEvent mette l'event object in stato "segnalato", che a sua volta fa si che la chiamata a WaitForSingleObject nella thread procedure ritorni e quindi che il thread cominci ad essere eseguito. Quando l'utente seleziona il comando "stop thread", noi settiamo il valore della variabile globale "EventStop" a 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

Cio' blocca il thread e salta di nuovo alla chiamata a WaitForSingleObject. Notate che non dobbiamo resettare manualmente l'event object in stato "non-segnalato", poiche' abbiamo specificato il parametro bManualReset della chiamata a CreateEvent come FALSE.


Disclaimer

I documenti qui pubblicati sono da considerarsi pubblici e liberamente distribuibili, a patto che se ne citi la fonte di provenienza. Tutti i documenti presenti su queste pagine sono stati scritti esclusivamente a scopo di ricerca, nessuna di queste analisi è stata fatta per fini commerciali, o dietro alcun tipo di compenso. I documenti pubblicati presentano delle analisi puramente teoriche della struttura di un programma, in nessun caso il software è stato realmente disassemblato o modificato; ogni corrispondenza presente tra i documenti pubblicati e le istruzioni del software oggetto dell'analisi, è da ritenersi puramente casuale. Tutti i documenti vengono inviati in forma anonima ed automaticamente pubblicati, i diritti di tali opere appartengono esclusivamente al firmatario del documento (se presente), in nessun caso il gestore di questo sito, o del server su cui risiede, può essere ritenuto responsabile dei contenuti qui presenti, oltretutto il gestore del sito non è in grado di risalire all'identità del mittente dei documenti. Tutti i documenti ed i file di questo sito non presentano alcun tipo di garanzia, pertanto ne è sconsigliata a tutti la lettura o l'esecuzione, lo staff non si assume alcuna responsabilità per quanto riguarda l'uso improprio di tali documenti e/o file, è doveroso aggiungere che ogni riferimento a fatti cose o persone è da considerarsi PURAMENTE casuale. Tutti coloro che potrebbero ritenersi moralmente offesi dai contenuti di queste pagine, sono tenuti ad uscire immediatamente da questo sito.

Vogliamo inoltre ricordare che il Reverse Engineering è uno strumento tecnologico di grande potenza ed importanza, senza di esso non sarebbe possibile creare antivirus, scoprire funzioni malevole e non dichiarate all'interno di un programma di pubblico utilizzo. Non sarebbe possibile scoprire, in assenza di un sistema sicuro per il controllo dell'integrità, se il "tal" programma è realmente quello che l'utente ha scelto di installare ed eseguire, né sarebbe possibile continuare lo sviluppo di quei programmi (o l'utilizzo di quelle periferiche) ritenuti obsoleti e non più supportati dalle fonti ufficiali.