| ||
In the last section of this chapter, I'll consider an aspect rarely covered in programming literaturetimers in console applications. It should be pointed out that, by doing so, I am rushing slightly ahead, because I consider timers in console applications before timers in GUI applications.
The main method of creating a timer is by using the SetTimer function. Later, I'll cover this function in more detail. There are two modes for setting a timer. The first mode is the one in which the last function parameter is zero. In this case, the current window (or, to be more precise, its function) will receive the WM_TIMER message with a predefined time period defined by the third parameter. When operating in the second mode, the last parameter will point to the function that will be called with the predefined periodicity. However, this function isn't suitable for a console application, because the WM_TIMER message is dispatched to a window by the DispatchMessage function employed in the message-processing loop. This, the use of this function for console applications is problematic .
For console applications, it is expedient to use the timeSetEvent function, the parameters of which are briefly outlined here:
First parameterTimer delay (from your viewpoint, this time is the same as the period between two timer calls)
Second parameterTimer precision (the priority of message sending)
Third parameterAddress of the procedure being called'
Fourth parameterAddress sent to the procedure
Fifth parameterType of the procedure call: one time or periodic
If the function has completed successfully, then the timer identifier will be returned into EAX .
The calling procedure itself receives the following five parameters:
First parameterTimer identifier
Second parameterNot used
Third parameter Dat (see the description of the timeSetEvent function)
Fourth and fifth parametersNot used
To remove the timer, use the timeKillEvent function that receives the timer identifier as its parameter.
.586P ; Flat memory model .MODEL FLAT, stdcall ; Constants STD_OUTPUT_HANDLE equ -11 STD_INPUT_HANDLE equ -10 TIME_PERIODIC equ 1 ; Type of the timer call ; Color attributes FOREGROUND_BLUE equ 1h ; Blue foreground color FOREGROUND_GREEN equ 2h ; Green foreground color FOREGROUND_RED equ 4h ; Red foreground color FOREGROUND_INTENSITY equ 8h ; Increased intensity BACKGROUND_BLUE equ 10h ; Blue background color BACKGROUND_GREEN equ 20h ; Green background color BACKGROUND_RED equ 40h ; Red background color BACKGROUND_INTENSITY equ 80h ; Increased intensity COLl = 2h+8h ; Color of the displayed text ; Prototypes of external procedures EXTERN wsprintfA:NEAR EXTERN GetStdHandle@4:NEAR EXTERN WriteConsoleA@20:NEAR EXTERN SetConsoleCursorPosition@8:NEAR EXTERN SetConsoleTitleA@4:NEAR EXTERN FreeConsole@0:NEAR EXTERN AllocConsole@0:NEAR EXTERN CharToOemA@8:NEAR EXTERN SetConsoleCursorPosition@8:NEAR EXTERN SetConsoleTextAttribute@8:NEAR EXTERN ReadConsoleA@20:NEAR EXTERN timeSetEvent@20:NEAR EXTERN timeKillEvent@4:NEAR EXTERN ExitProcess@4:NEAR ; INCLUDELIB directives for the linker includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib includelib c:\masm32\lib\winmm.lib ;----------------------------------------------- COOR STRUC X WORD ? Y WORD ? COOR ENDS ; Data segment _DATA SEGMENT HANDL DWORD ? HANDL1 DWORD ? STR2 DB "Example of a timer in a console application", 0 STR3 DB 100 dup(0) FORM DB "Number of timer calls: %lu", 0 BUF DB 200 dup(?) NUM DWORD 0 LENS DWORD ? ; Number of displayed characters CRD COOR <?> ID DWORD ? ; Timer identifier HWND DWORD ? _DATA ENDS ; Code segment _TEXT SEGMENT START: ; Create a console ; First, release the existing console CALL FreeConsole@0 CALL AllocConsole@0 ; Get the HANDL1 input handle PUSH STD_INPUT_HANDLE CALL GetStdHandle@4 MOV HANDL1, EAX ; Get the HANDL output handle PUSH STD_OUTPUT_HANDLE CALL GetStdHandle@4 MOV HANDL, EAX ; Specify the console window header PUSH OFFSET STR2 CALL SetConsoleTitleA@4 ; Specify the color attributes for the output text PUSH COL1 PUSH HANDL CALL SetConsoleTextAttribute@8 ; Set the timer PUSH TIME_PERIODIC ; Periodic call PUSH 0 PUSH OFFSET TIME ; Procedure being called by the timer PUSH 0 ; Precision of the timer call PUSH 1000 ; Calls per second CALL timeSetEvent@20 MOV ID, EAX ; Wait for the string input PUSH 0 PUSH OFFSET LENS PUSH 200 PUSH OFFSET BUF PUSH HANDL1 CALL ReadConsoleA@20 ; Close the timer PUSH ID CALL timeKillEvent@4 ; Close the console CALL FreeConsole@0 PUSH 0 CALL ExitProcess@4 ; String - [EBP+08H] ; Length in EBX LENSTR PROC ENTER 0, 0 PUSH EAX ;---------------------- CLD MOV EDI, DWORD PTR [EBP+08H] MOV EBX, EDI MOV ECX, 100 ; Limit the string length XOR AL, AL REPNE SCASB ; Find the 0 character SUB EDI, EBX ; String length including 0 MOV EBX, EDI DEC EBX ;---------------------- POP EAX LEAVE RET 4 LENSTR ENDP ; Procedure called by the timer TIME PROC PUSHA ; Save all registers ; Set the cursor position MOV CRD.X, 0 MOV CRD.Y, 10 PUSH CRD PUSH HANDL CALL SetConsoleCursorPosition@8 ; Fill the STR3 string PUSH NUM PUSH OFFSET FORM PUSH OFFSET STR3 CALL wsprintfA ADD ESP, 12 ; Restore the stack ; Reencode the STR3 string PUSH OFFSET STR3 PUSH OFFSET STR3 CALL CharToOemA@8 ; Display the string with the timer call number PUSH OFFSET STR3 CALL LENSTR PUSH 0 PUSH OFFSET LENS PUSH EBX PUSH OFFSET STR3 PUSH HANDL CALL WriteConsoleA@20 INC NUM POPA RET 20 ; Exit and release the stack TIME ENDP _TEXT ENDS END START
The program presented in Listing 8.5 will output the counter value into the window. This value will be incremented by one once per second.
This chapter was opened with considerations about the command line; however, until now, I haven't explained how to work with the command line. Well, everything is easy here. There is an API function called GetcommandLine , which returns the pointer to the command string. This function operates similarly both for console applications and for GUI applications. The program presented in Listing 8.6 prints the command-line parameters. As you may have guessed, the first parameter is the program's full name .
; The program for the output of the command-line parameters .586P ; Flat memory model .MODEL FLAT, stdcall ; Constants CTL_OUTPUT_HANDLE equ -11 ; Prototypes of external procedures EXTERN GetStdHandle@4:NEAR EXTERN WriteConsoleA@20:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetCommandLineA@0:NEAR ; Include directives for the linker includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib ;------------------------------------- ; Data segment _DATA SEGMENT BUF DB 100 dup(0) LENS DWORD ? ; Number of displayed characters NUM DWORD ? CNT DWORD ? HANDL DWORD ? _DATA ENDS ; Code segment _TEXT SEGMENT START: ; Get the HANDLE output handle PUSH STD_OUTPUT_HANDLE CALL GetStdHandle@4 MOV HANDL, EAX ; Get the number of parameters CALL NUMPAR MOV NUM, EAX MOV CNT, 0 ;------------------------------------- ; Output the command-line parameters LL1: MOV EDI, CNT CMP NUM, EDI JE LL2 ; Parameter number INC EDI MOV CNT, EDI ; Get the parameter number EDI LEA EBX, BUF CALL GETPAR ; Get the parameter length PUSH OFFSET BUF CALL LENSTR ; Line feed in the end MOV BYTE PTR [BUF+EBX], 13 MOV BYTE PTR [BUF+EBX+1], 10 MOV BYTE PTR [BUF+EBX+2], 0 ADD EBX, 2 ; String output PUSH 0 PUSH OFFSET LENS PUSH EBX PUSH OFFSET BUF PUSH HANDL CALL WriteConsoleA@20 JMP LL1 LL2: PUSH 0 CALL ExitProcess@4 ; String - [EBP+08H] ; Length in EBX LENSTR PROC PUSH EBP MOV EBP, ESP PUSH EAX ;--------------------- CLD MOV EDI, DWORD PTR [EBP+08H] MOV EBX, EDI MOV ECX, 100 ; Limit the string length XOR AL, AL REPNE SCASB ; Find the 0 character SUB EDI, EBX ; String length including 0 MOV EBX, EDI DEC EBX ;--------------------------- POP EAX POP EBP RET 4 LENSTR ENDP ; Define the number of parameters ( EAX) NUMPAR PROC CALL GetCommandLineA@0 MOV ESI, EAX ; Pointer to the string XOR ECX, ECX ; Counter MOV EDX, 1 ; Indicator LI: CMP BYTE PTR [ESI], 0 JE L4 CMP BYTE PTR [ESI], 32 JE L3 ADD ECX, EDX ; Parameter number MOV EDX, 0 JMP L2 L3: OR EDX, 1 L2: INC ESI JMP L1 L4: MOV EAX, ECX RET NUMPAR ENDP ; Get the parameter ; EBXPoints to the buffer in which the parameter will be loaded ; Zero-terminated string is loaded into the buffer ; EDIParameter number GETPAR PROC CALL GetCommandLineA@0 MOV ESI, EAX ; Pointer to the string XOR ECX, ECX ; Counter MOV EDX, 1 ; Indicator L1: CMP BYTE PTR [ESI], 0 JE L4 CMP BYTE PTR [ESI], 32 JE L3 ADD ECX, EDX ; Parameter number MOV EDX, 0 JMP L2 L3: OR EDX, 1 L2: CMP ECX, EDI JNE L5 MOV AL, BYTE PTR [ESI] MOV BYTE PTR [EBX], AL INC EBX L5: INC ESI JMP L1 L4: MOV BYTE PTR [EBX], 0 RET GETPAR ENDP _TEXT ENDS END START
I strongly recommend that you make sure you understand the working algorithms of the NUMPAR and GETPAR procedures.
Note that to assemble and link the program from Listing 8.6 using TASM, in addition to the standard modifications that you know already, it is necessary to add the @@ locality prefix for matching labels and add the locals directive to the start of the program.
| ||