| ||
This section is dedicated to descriptions of the keyboard and mouse events in console applications. These capabilities make console applications flexible and powerful, considerably extending the range of tasks that can be carried out in this mode.
Before proceeding any further, however, consider one unusual but useful API function. This is the wsprintfA function. I specially emphasize that this is an API function, which is provided to applications by the operating system. This function is an analogue of the sprintf built-in C function. Its first parameter is the pointer to the buffer, into which the result of formatting is loaded. The second argument is the pointer to the format string (e.g., " Numbers : %lu, %1u "). Then follow the pointers to parameters (or parameters themselves if they are numeric). The number of parameters is limited only by the contents of the format string. Now, the most important point must be considered : Because the number of parameters is undefined, you have to clear the stack on your own. The example illustrating the use of this function will be provided later. It is also necessary to mention that for the IMPORT32.LIB (TASM32) library, the prototype of this function will be _wsprintfa instead of wsprintfA . Finally, note that if the function has completed successfully, then the length of the copied string will be returned into EAX .
The ReadConsole Input function serves as a basis for getting information about the keyboard and mouse in the console mode. Parameters of this function are briefly described here:
FirstDescriptor of the console's input buffer
SecondThe pointer to the structure (or an array of structures) containing information about the events that occurred in this console (later, I will cover this structure in more detail)
ThirdThe number of received records (structures)
FourthThe pointer to the double word containing the number of records actually received
Now consider the structure containing information about the console event in more detail. First, I'd like to note that in C this structure is defined using the UNION data type (data types will be covered in detail in Chapter 12). In my opinion, frequent use of this word obscures its actual meaning. Therefore, when describing this structure, I will do without the STRUCT and UNION keywords. Also, note that this block of data starts with the double word whose least significant word defines the event type. Depending on the value of this word, all further bytes (18 at most) will be interpreted in a specific way. If you are already acquainted with various structures used in C and macroassembler, you must now understand why UNION is relevant here.
However, let's return to the event type. The total number of events reserved by the system is five:
KEY_EVENT equ 1h ; Keyboard event MOUSE_EVENT equ 2h ; Mcuse event WINDOW_BUFFER_SIZE_EVENT equ 4h ; Window size has changed MENU_EVENT equ 3h ; Reserved FOCUS_EVENT equ 10h ; Reserved
Now, it is time to describe the values of other bytes of this structure depending on the event that took place. The description of the KEY_EVENT event is provided in Table 8.1.
Offset | Length | Value |
---|---|---|
+4 | 4 | The field value is greater than zero when a key is pressed. |
+8 | 2 | This shows the number of repetitions when a key is held. |
+10 | 2 | This field holds the virtual code of the key. |
+12 | 2 | Here is the scan code of the key. |
+14 | 2 | For the ReadConsolelnputA function, the least significant byte is equal to the ASCII code of the key. For the ReadConsoleInputw function, the word contains the Unicode code of the key. |
+16 | 4 | This contains the status of the control keys. It can represent the sum of the following constants:
|
The meaning of these constants is obvious. |
The description of the MOUSE_EVENT event is provided in Table 8.2.
Offset | Length | Value |
---|---|---|
+4 | 4 | The least significant word is the X-coordinate of the mouse, and the most significant word is the Y-coordinate. |
+8 | 4 | This field describes the state of the mouse buttons . The first bit corresponds to the left button, the second bit corresponds to the right button, and the third bit corresponds to the third button. (Recall that bit numbering starts from zero.) If a bit is set to one, this means that the key is pressed. |
+12 | 4 | This shows the state of control keys, similar to the field of the previous table. |
+16 | 4 | This field can contain the following values:
|
Consider the WINDOW_BUFFER_SIZE_EVENT event.
The double word containing the new size of the console window resides by the offset +4. The least significant word is the X-size, and the most significant word is the Y-size. Note that when you are dealing with a console window, all size and coordinate values are given in "symbolic" units.
As relates to the last two events, the most significant word is the one located by the offset +4. Listing 8.4 provides a simple example illustrating the processing of console events.
.586P ; Flat memory model .MODEL FLAT, stdcall ; Constants STD_OUTPUT_HANDLE equ -11 STD_INPUT_HANDLE equ -10 ; Event type KEY_EV equ 1h MOUSE_EV equ 2h ; ConstantsKeyboard status RIGHT_ALT_PRESSED equ 1h LEFT_ALT_PRESSED equ 2h RIGHT_CTRL_PRESSED equ 4h LEFT_CTRL_PRESSED equ 8h SHIFT_PRESSED equ l0h NUMLOCK_ON equ 20h SCROLLLOCK_DN equ 40h CAPSLOCK_ON equ 80h ENHANCED_KEY equ l00h ; 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 SetConsoleTextAttribute@8:NEAR EXTERN ReadConsoleInputA@16:NEAR EXTERN ExitProcess@4:NEAR ; INCLUDELIB directives includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib ;------------------------------------------ ; Structure for defining events COOR STRUC X WORD ? Y WORD ? COOR ENDS ; Data segment _DATA SEGMENT HANDL DWORD ? HANDL1 DWORD ? TITL DB "Processing mouse events", 0 BUF DB 200 dup(?) LENS DWORD ? ; Number of displayed characters CO DWORD ? FORM DB "Coordinates: %u %u " CRD COOR <?> STR1 DB "Press ESC to exit", 0 MOUS_KEY WORD 9 dup(?) _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 ; Create the console window header PUSH OFFSET TITL CALL SetConsoleTitleA@4 ;*********************************** ; String reencoding PUSH OFFSET STR1 PUSH OFFSET STR1 CALL CharToOemA@8 ; String length PUSH OFFSET STR1 CALL LENSTR ; Display the string PUSH 0 PUSH OFFSET LENS PUSH EBX PUSH OFFSET STR1 PUSH HANDL CALL WriteConsoleA@20 ; Waiting loop Mouse move or double-click LOO: ; Cursor coordinates MOV CRD.X, 0 MOV CRD.Y, 10 PUSH CRD PUSH HANDL CALL SetConsoleCursorPosition@8 ; Read one record about the event PUSH OFFSET CO PUSH 1 PUSH OFFSET MOUS_KEY PUSH HANDL1 CALL ReadConsoleInputA@16 ; Check events from the mouse CMP WORD PTR MOUS_KEY, MOUSE_EV JNE LOO1 ; Convert the mouse coordinates to a string MOV AX, WORD PTR MOUS_KEY+6 ; Y-mouse ; Copy and res et the most significant bits to zero MOVZX EAX, AX PUSH EAX MOV AX, WORD PTR MOUS_KEY+4 ; X-mouse ; Copy and reset the most significant bits to zero MOVZX EAX, AX PUSH EAX PUSH OFFSET FORM PUSH OFFSET BUF CALL wsprintfA ; Restore the stack ADD ESP, 16 ; Convert the output string PUSH OFFSET BUF PUSH OFFSET BUF CALL CharToOemA@8 ; String length PUSH OFFSET BUF CALL LENSTR ; Display the cursor coordinates PUSH 0 PUSH OFFSET LENS PUSH EBX PUSH OFFSET BUF PUSH HANDL CALL WriteConsoleA@20 JMP LOO ; Jump to the starting point of the loop LOO1: ; Check for keyboard events CMP WORD PTR MOUS_KEY, KEY_EV JNE LOO ; There was a keyboard event; which one? CMP BYTE PTR MOUS_KEY+14, 27 JNE LOO ;************************** ; Close the console CALL FreeConsole@0 PUSH 0 CALL ExitProcess@4 RET ; The procedure for determining the string length ; 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 _TEXT ENDS END START
Consider this program in more detail.
First, consider the wsprintfA function. As I have already mentioned, this is an unusual function.
It has an unlimited number of parameters. The first two parameters are required. The first parameter is the pointer to the buffer, into which the resulting string will be copied. The next parameter is the pointer to the format string. The format string can contain text and the format of the parameters for output. The fields containing information about the parameters start from the % character. The format of these fields is practically the same as the format of the fields used in standard C library functions, such as printf and sprintf . The only exception is the lack of real numbers in the format for the wsprintf function. There is no need to consider this format in detail. It is only necessary to note that every field in the format string corresponds to a parameter (starting from the third one). For example, in the program under consideration, the format string appeared as follows : " Coordinates: %u %u ". This means that two numeric parameters of the WORD type will be pushed into the stack. In fact, you will have pushed into the stack two double words, having previously reset the most significant words to zero. For such an operation, it is expedient to use the MOVZX microprocessor command, which copies the second operand into the first so that the bits of the most significant word are filled with zeroes. If the parameters were double words, then instead of the %u field you would use %lu . If the field of the format string defines a string parameter, for example, " %S ", then the pointer to that string must be pushed into the stack (which is natural). [i]
Because the function has no information about the number of parameters that can be passed to it, the developers decided not to complicate the code of the function and left the task of clearing the stack to the programmer's discretion. [ii] The stack is released by the ADD ESP, N command, where N is the number of bytes to be deleted.
Now, turn your attention to the ReadConsoleInputA function. To the information already provided about it, I'll only add that if the event buffer is empty, the function will wait until any event occurs to the console window and will return the control only after such an event. Besides this, you can specify that the function should return not one but two or more records on the events that occurred to the console window. In this case, several information records will be pushed into the stack. However, I won't concentrate on this aspect.
As usual, I'd like to explain how to compile this program using TASM32. As before, it is necessary to remove all @N entries, specify the IMPORT32.LIB library in the INCLUDELIB directive, and replace wsprintfA with _wsprintfA .
[i] I can't help mentioning that in some publications on Assembly language, I have encountered information that says all parameters for this function pushed into the stack are pointers. As you can see, this statement is generally incorrect.
[ii] Naturally, the C compiler carries out this task automatically.
| ||