News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Windows API32 documentation

Started by RuiLoureiro, March 26, 2005, 02:51:14 PM

Previous topic - Next topic

hutch--

Something that is worth keeping in mind is that a "pointer" is nothing more than an address stored in a variable. If you have a procedure that has a local variable that you need the address for, you do it something like this,


    LOCAL MyVar :DWORD    ; allocate the local variable
    LOCAL MyPtr :DWORD    ; allocate a pointer for it

    lea eax, MyVar               ; load the address of the variable into the EAX register
    mov MyPtr, eax              ; store that address in another variable


With this done, you have the address available in a variable instead of having to obtain it every time you need to use it.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

raymond

QuoteHe follows mathematics, may be because i was a teacher of mathematics in a high school for 6 years

With that background, you may be interested in a site maintained by another high school math teacher (England). Look at his "Project Euler". You may even enjoy a challenge with your son.

http://www.mathschallenge.net/

Raymond
When you assume something, you risk being wrong half the time
http://www.ray.masmcode.com

RuiLoureiro

Hi

Xor Stance,
   I know DOS 16-bits asm. I have Icztutes (downloaded in Nov\2003);
I don't like C. I think, the thing i have to do can be done in console mode.
Ok, LP: stands for long pointer. There are many words\names\members\ ??? that begin with lp. But in Win32 it says nothing. All addresses are 32 bites. No ?
Thanks
-----------------------
tenkey,
   I invented those expressions above. Your image is funny. But when we use
GlobalAlloc to alloc fixed memory the handle=pointer. So, we don't need to use conversion functions and «"cast" a handle to a pointer» means ... nothing, I think. If the function as a handle as output and i need a pointer, i should prefer «convert» than «cast» ( input: handle, output: pointer). About «cast» i like the «weather forecast» ! We should play ... to go ahead. Thanks
-----------------------
Mr. Hutch—
   Good little topic.
   This is a particular question so importante. It because we are using the stack. In DOS 16-bites i never used lea instructions. All pointers were passed by registers. Before the call, i used «mov   ebx, offset XXX» ... then call YYY.
I took it down. Thanks

We could do this, too, no ?

.data
   MyVar   dd 100

.code   
   pMyVar    dd offset MyVar      ; pointer for MyVar in CODE

...
   invoke   ExecProc, pMyVar
;               ---------------------------
ExecProc   proc   pMyVar:DWORD   ; this pMyVar is local but is already pointer

      ret
ExecProc   endp
------------------------
Vortex,
        This code does nothing in my WinXP. It enter and exit. Why ?

.386
.model flat, stdcall
option casemap:none

include     \masm32\include\windows.inc
include     \masm32\include\kernel32.inc
include     \masm32\include\masm32.inc      ; StdOut, StdIn
includelib  \masm32\lib\kernel32.lib
includelib  \masm32\lib\masm32.lib

.data
msg1    db 'Please type your name',13,10,0
msg2    db 'Nice to see you ',0

.data?
buffer db 100 dup(?)

.code
start:
   invoke StdOut,ADDR msg1
   invoke StdIn,ADDR buffer,100       ; receive text input
   invoke StdOut,ADDR msg2
   invoke StdOut,ADDR buffer
   invoke ExitProcess,0
END start
--------------------------------------------
Don't  we need AllocConsole  or  CreateProcess, first ?

Does anyone know a good book about it ?

Thanks
Stay well

RuiLoureiro

Hi
reminder:   «The Workshop is where general purpose questions and answers are posted»

Point 1.   
               Where i before wrote «little topic» we should read «short topic».

Point 2.
                I am studying the files which came with masm32. In particular in 3
   directories: INCLUDE, LIB and M32LIB. That files have many names of
   procedures which i think it is hard to follow.

   What i am seeing is, for example, GetDC, GetCursor, GetKeyboard...
   GetMessage... GetWindow... etc. etc. So, we jump from DC to Cursor, to
   Messages, Windows, etc. etc. tons of Gets.
   (I didn't see GetPotatoes because I have problems with my glasses!)

   The names for the functions was constructed beginning with «ACTION»
   { Get, Set, Kill, etc. } and then «OBJECT» {DC, Cursor, Message,
     Window, etc. }. So, the model here is «ActionObject».

Point 3.   
               When i want something, the first idea i have is about one «thing»,
   not the action. Then, i think «what i need to do with that thing»?
   When i choose the thing, i begin to think about the necessary
   actions: I need «Get», i need «Set», i need «Verify», i need «Kill»,
   i need «Convert», etc. etc. So, in my model, the function names          would be «ActionObject»:


DC     object:   {DCSet, DCGet, DC???,...},
Cursor object:   {CursorGet, CursorSet, CursorHide, Cursor???,...}
Window object:    {WindowGet, WindowSet, Window???, ...}
File   object:   {FileRead, FileWrite, FilePosition, FileClose, FileOpen,
        FileExtract, FileInsert, FileAdd, File??? }

In this way, we had a collection of objects and for each one we had a set of actions. So, when we want to write a program, we begin thinking something like this: what objects i need ? what do i want to do with it ? Then we go to look for that object and see what actions are defined for it. If we need more actions we would write more procedures to those actions we need.

Is it hard to follow ?

Point 4.   
                For me, the model «ActionObject» is hard to follow. I go to see one
   thing and i find many things around that has nothing to see with it.
   My eyes are obliged to see not connected things.

Stay well
Regards

hutch--

RuiLoureiro,

Until you get the documentation that various people have advised you to get, you are flying in the dark. The WIN32.HLP file and / or the MSDN help files from the PLATFORMSDK. Once you have the reference material you are in a position to use the assistance that various members have offered.

As far as the architecture of MASM32 and what files are in what directories, try reading the technical data that comes with the project as it will help you a lot there. The LIB and INCLUDE directories hold mainly import libraries for the functions that are in various Windows system DLLs but they also have the static library for the MASM32 library and the floating point library that Ray has written. The INCLUDE directory has the INCLUDE files that comtain the prototypes for the procedures in the matching libraries.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

Vortex

QuoteDon't  we need AllocConsole  or  CreateProcess, first ?

No, you don't need AllocConsole if you build your project as console application.

QvasiModo

Quote from: RuiLoureiro on April 05, 2005, 11:18:55 AM
Point 3.   
               When i want something, the first idea i have is about one «thing»,
   not the action. Then, i think «what i need to do with that thing»?
   When i choose the thing, i begin to think about the necessary
   actions: I need «Get», i need «Set», i need «Verify», i need «Kill»,
   i need «Convert», etc. etc. So, in my model, the function names          would be «ActionObject»:


DC     object:   {DCSet, DCGet, DC???,...},
Cursor object:   {CursorGet, CursorSet, CursorHide, Cursor???,...}
Window object:    {WindowGet, WindowSet, Window???, ...}
File   object:   {FileRead, FileWrite, FilePosition, FileClose, FileOpen,
        FileExtract, FileInsert, FileAdd, File??? }

In this way, we had a collection of objects and for each one we had a set of actions. So, when we want to write a program, we begin thinking something like this: what objects i need ? what do i want to do with it ? Then we go to look for that object and see what actions are defined for it. If we need more actions we would write more procedures to those actions we need.

Is it hard to follow ?

I bet you love C++, then... ;)

    ReturnValue = Object.Action(Parameters);

pbrennick

Although it is too late to change now; I find his point to be well stated.

Paul

Vortex

RuiLoureiro,

If you build your executable as a GUI application, here is how to proceed with AllocConsole

.386
.model flat,stdcall
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\masm32.inc

includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\masm32.lib

.data
msg           db 'Press RETURN to exit',0
.data?
buffer db 128 dup(?)
.code
start:
invoke AllocConsole
invoke StdOut,ADDR msg
invoke StdIn,ADDR buffer,128
invoke FreeConsole
invoke ExitProcess,0

END start

Xor Stance

buffedb I got a problem, in ANSI how do you make that symbol? []

QvasiModo

Quote from: Xor Stance on April 06, 2005, 09:23:15 PM
buffedb I got a problem, in ANSI how do you make that symbol? []
I think it's a TAB character that got printed wrong...

RuiLoureiro

#26
Hi all

First, I have a problem with my internet. I hope to solve it soon.
-----------------------------------------------------------------------------------------------------------------
I wrote an example that use ReadConsoleInput and WriteConsoleOutputCharacter.
I have problems to access INPUT_RECORD structure. Thank you
-------------------------------------------------------------------------------------------------------------------------------------------

.386
.model flat,stdcall
option casemap:none
;----------------------------------------------------------------------------------------------
include \masm32\include\windows.inc
;-------------------------------------------------
INPUT_RECORD STRUCT
EventType WORD  ?
   UNION              ; Start the UNION
     KeyEvent     KEY_EVENT_RECORD <>
     MouseEvent     MOUSE_EVENT_RECORD <>
     WindowBufferSizeEvent  WINDOW_BUFFER_SIZE_RECORD <>
     MenuEvent     MENU_EVENT_RECORD <>
     FocusEvent     FOCUS_EVENT_RECORD <>
   ENDS
INPUT_RECORD ENDS
lINPUT_RECORD     equ SIZEOF INPUT_RECORD
;--------------------------------------------------------------------------------------------------------------------------------------
;  THIS IS THE STRUCTURE OF EACH RECORD IN THE INPUT BUFFER when a key event happen
;
;KEY_EVENT_RECORD       STRUCT        ; this structure is in windows.inc
;         EventType WORD  ?              ; 0 This belongs to INPUT_RECORD ?
;  bKeyDown          DWORD ?              ; +2
;  wRepeatCount         WORD  ?              ; +6
;  wVirtualKeyCode    WORD  ?              ; +8
;  wVirtualScanCode  WORD  ?              ;+10
;  uChar              CHARTYPE <>       ;+12   ; Here is one case only UnicodeChar or AsciiChar ?
;  dwControlKeyState DWORD ?               ; ???
;KEY_EVENT_RECORD       ENDS
;
;CHARTYPE             UNION         ; this structure is in windows.inc
; UnicodeChar    WORD ?
; AsciiChar          db ?
;CHARTYPE             ENDS
;
; mov    al, _InputBuffer.uChar.AsciiChar    ; it gives an error
;
;******************************************************************************
include \masm32\include\kernel32.inc
include \masm32\include\masm32.inc
; ---------------------------------------------------------------------------------------------
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\masm32.lib
;-------------------------------------------------------------------------------------------
.data

  _CurCOL_LIN          dd 00000000              ; current LIN + COL
;-------------------------------------------------------------------------------------------
.data?
   _hInputBuffer        dd ?                     ; input handle
   _hScreenBuffer       dd ?                     ; screen handle
;******************************************************************************
; HOW CAN I CREATE **a buffer for 20 INPUT_RECORDS** using the structure INPUT_RECORD ?
;******************************************************************************
  _InputBuffer              db 20 * 18 dup(?)         ; input buffer 20 records
  _NumInpRecord        dd ?                             ; n chars read
;-----------------------------------------------------------------------------------   
  _PrintBuffer              db 100 dup (?)               ; output buffer
  _NumWriteChar        dd ?                              ; n chars written
;----------------------------------------------------------------------------------------
.code
start:
      invoke  GetStdHandle, STD_INPUT_HANDLE ; Get STANDARD INPUT BUFFER handle
   cmp     eax, INVALID_HANDLE_VALUE
   je      eStart
   mov     _hInputBuffer, eax                              ; input buffer handle

   ; ---------------------------------
   ; Get STANDARD SCREEN BUFFER handle
   ; ---------------------------------
      invoke  GetStdHandle, STD_OUTPUT_HANDLE ; Get STANDARD SCREEN BUFFER handle
   cmp     eax, INVALID_HANDLE_VALUE
   je      ibStart
   mov     _hScreenBuffer, eax                             ; screen buffer handle

      invoke    FlushConsoleInputBuffer, _hInputBuffer      ; Clear Input buffer
TryReadEvent:
         ; -----------------------------
         ; Read INPUT BUFFER
         ; -----------------------------
         invoke    ReadConsoleInput, _hInputBuffer,             ; handle
                                    ADDR _InputBuffer,            ; Record buffer
                                    1,                            ; Read 1 record
                                    ADDR _NumInpRecord            ; Records Read
         cmp      eax, 0
         je       sbStart         
         ; --------------------------------
         ; How many read records
         ; --------------------------------
         mov    eax, _NumInpRecord
         cmp    eax, 0
         je     TryReadEvent

         ; ------------------
         ; What Event ?
         ; ------------------
         movzx  edx, word ptr [_InputBuffer + 0]                    ; EventType
         cmp      edx, KEY_EVENT
         jne        TryReadEvent

         ; ---------------------------------
         ; Process Keyboard Event
         ; ---------------------------------
         mov    ebx, dword ptr [_InputBuffer + 2]                   ; pressed\released
         movzx  ecx, word ptr [_InputBuffer + 6]                    ; repeat count
         movzx  ecx, word ptr [_InputBuffer + 8]                    ; VirtualKeyCode
         movzx  edx, word ptr [_InputBuffer + 10]                  ; VirtualScanCode
         mov    al,  byte ptr [_InputBuffer + 12]                       ; AsciiChar ?
         mov    esi, dword ptr [_InputBuffer + 13]                   ; ControlKeyState  ?
         mov    edi, dword ptr [_InputBuffer + 14]                   ; ControlKeyState ?

         ; ----------------------
         ; If released, next
         ; ----------------------
         cmp    ebx, FALSE
         je     TryReadEvent                                        ; released

         cmp    dl, 13
         je     short sbStart                                      ; RETURN -> exit
         ; -------------------------
         ; Set Key into _PrintBuffer
         ; -------------------------
         mov    byte ptr _PrintBuffer, dl                           ; ASCII is IN DL !!!
TryWrite:         
         invoke WriteConsoleOutputCharacter, _hScreenBuffer,        ; handle
                                    ADDR _PrintBuffer,                          ; print buffer
                                    1,                                          ; write 1 char
                                    _CurCOL_LIN,                                ; COL + LIN (word)
                                    ADDR _NumWriteChar                          ; Number write
         cmp    eax, 0
         je     sbStart                                    ; error -> exit

         cmp    _NumWriteChar, 1
         jne    TryWrite                                          ; try write
         jmp    TryReadEvent                                ; begin
;---------------------------------------------------------------------
;                             E N D
;---------------------------------------------------------------------         
sbStart: invoke CloseHandle, _hScreenBuffer          ; Close screen buffer
ibStart: invoke CloseHandle, _hInputBuffer             ; Close input buffer
eStart:
   invoke ExitProcess, 0
END start

-----------------------------------------------------------------------------
QvasiModo,
   You lost the bet ! I don't like C.

Vortex,
    Ok, it is assumed and understood ! Thank you for your help, Vortex.
    I compiled your example (i called cons2.asm )in 4 ways:
1. 
\MASM32\BIN\Ml.exe /c /coff              CONS2.asm
\MASM32\BIN\Link.exe /SUBSYSTEM:CONSOLE  CONS2.obj
2.
\MASM32\BIN\Ml.exe /c /coff              CONS2.asm
\MASM32\BIN\Link.exe /SUBSYSTEM:WINDOWS  CONS2.obj

3. Using QEditor
   3.1   - Project -> Console assemble and link
   3.2   - Project -> Build all

In all 4 cases it works in the same way as i expected.

Stay well
RuiLoureiro

RuiLoureiro

                                                 «All exact science is dominated by the idea of approximation»
                                                                                                               «Bertrand Russell»
Hi
       All comments are welcome.

1.   I want to use the ReadConsoleInput function to read the keyboard in a console application. 
     I am interested only in the  KEY_EVENT_RECORDs.


2.  Documentation:
                           ( To clean the input buffer we use FlushConsoleInputBuffer )

  2.A) Format of the ReadConsoleInput function:
   ReadConsoleInput reads data from a console input buffer and removes it from the buffer.

  Format: ReadConsoleInput (  hConsoleInput,   ; handle of a console input buffer
                                             lpBuffer,                   ; address of the buffer for read data
                                             nLength,                   ; number of records to read
                                             lpNumberOfEventsRead  ; address of number of records read )

Details:    lpBuffer:  Points to an INPUT_RECORD buffer that receives the input buffer data;
                 nLength:  Specifies the size, in input records, of the buffer pointed to by the lpBuffer;
                 lpNumberOfEventsRead: Points to a 32-bit variable.

  2.B) Input Record structure:   
       INPUT_RECORD {
            WORD EventType; 
             union { KEY_EVENT_RECORD KeyEvent;                     ; [ Only 1 of the following cases ]
                          MOUSE_EVENT_RECORD MouseEvent;
                          WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
                          MENU_EVENT_RECORD MenuEvent;           «discard »
                          FOCUS_EVENT_RECORD FocusEvent;                     «discard »
    } Event; }

      Taking into account  KEY_EVENT_RECORD, in this case,  the InputRecord structure is:

INPUT_RECORD       STRUCT                                         CHARTYPE        UNION
  EventType             WORD  ?              ; +0                     UnicodeChar    WORD ?
  KeyDown               DWORD ?             ; +2                     AsciiChar         db ?
  RepeatCount          WORD  ?              ; +6                    CHARTYPE        ENDS
  VirtualKeyCode       WORD  ?              ; +8
  VirtualScanCode      WORD  ?              ;+10
  Char                      CHARTYPE <>      ;+12             ; Only UnicodeChar or AsciiChar
  ControlKeyState      DWORD ?             ;+14
INPUT_RECORD         ENDS                  ;+18 bytes    imply each InputRecord = 18 bytes

3.  I tested this structure in my computer and i saw this ( i dont know why !):
     Position:      + 10              +12                 +14                +16             [ values in decimal ]
Key      VirtualScanCode   Char               AsciiCode      ControlKeyState
CapsLock          0014               003A                0000            0080  ; CAPSLOCK_ON  = 0080
Pg Up              0021               0049                 0000            0180  ; ENHANCED_KEY=0100
A                    0041               001E                 0041            0080
a                     0041               001E                 0061            0000   [ values in hexa ]

As we are seeing, the character «a» has the Ascii Code 61h in the position +14, but the ascii code of the key PgUp ( 49h ) is in the position +12.

So, i concluded  that  the  INPUT RECORD structure is:
INPUT_RECORD                  STRUCT                         
  EventType                       WORD   ?          ; +0        = KEY_EVENT  => key is the case       
  KeyDown                         DWORD ?          ; +2        pressed=TRUE, released = FALSE
  RepeatCount                    WORD   ?          ; +6                   
  VirtualKeyCode                  WORD  ?          ; +8
  VirtualScanCode                 WORD  ?          ;+10
  CharCode                          WORD  ?          ;+12          ???
  AsciiCode                          WORD  ?          ;+14          AsciiCode = low byte
  ControlKeyState                 WORD  ?          ;+16          ControlState
INPUT_RECORD                    ENDS              ;+18 bytes    imply each InputRecord = 18 bytes

4.  What RepeatCount is ?

   It  «specifies a count indicating that a key is being held down. For example, when a key is held down, you might get five events with this member equal to 1, one event with this member equal to 5, or multiple events with this member greater than or equal to 1.» [ this means two cases or three ???. The first produces 6 input records ??? ]

[ i conclude that  when  EventType= KEY_EVENT:
            if      NumberOfEventsRead=1 and RepeatCount=1 and KeyDown=TRUE
            than a key was pressed and we dont know if there are more InputRecords with this state;
            When a key is released there is only one InputRecord so i expect RepeatCount = 1, but ?]

5.  With this set of information, after  returning from ReadConsoleInput,  i think the procedure to get each key should do this:

              1º - test the word in the field EventType ( the first word );
              2º - test 32-bit variable NumberOfEventsRead to see if the function read records;
              3º - read and save the  AsciiCode and ControlKeyState  fields;
              4º - wait for a input record with KeyDown=FALSE and discarding all other input records
                    ( each time i press a key until i release, i want 1 code only for that key ).

6.  What happened  if  we specify  nLength = 10  ( number of records to read  ) and the function
     returns  with NumberOfEventsRead = 1  and  KeyDown=TRUE ? What the next InputRecord
     are if we use  FlushConsoleInputBuffer after the previous case ? It seems that we cannot read
     records with KeyDown=FALSE. But is this true ?

Stay well




RuiLoureiro

Hi
              I think no one is interested in console applications, but... perhaps...
   In the previous part of this topic i left some questions opened. No one said something. Meanwhile i completed my example to test the function ReadConsoleInput and the result is:  when i press a key i get:
                      Records read = 1; Key Event; state «released» ; Repetition is 0 .
when i move the mouse i get:
                      Records read = 1; not Key Event; state «released»;  Repetition is 15 / 9  ( it is variable) .

And  it  never returns from the procedure KeyWait to CharRead.
( i am trying to write my procs like ObjectAction )

Why  do i get  the state «released»Why  do i get  Repetition = 0  ?
Whats wrong ?

The procedures are: RCLEditStr, CharRead and KeyWait
--------------------------------------------------------------------------------------------------
RCLEditStr              proc   pRCLStr:DWORD, nLin:DWORD, nCol:DWORD
                                push   ebx
                                push   esi

                             [    ...    ]
                             invoke  CharRead, nLin, nCol             ; Read to AL, AH, DX
                             jc          _rRCLEditStr
                              ; -----------
                              ; Filter AL
                              ; -----------
_rRCLEditStr:           stc                                                  ; error
_eRCLEditStr:           pop   esi
                              pop   ebx
                              ret
;
; exit
; ----
_oRCLEditStr:          clc                                                  ; OK
                             pop   esi
                             pop   ebx
                             ret
RCLEditStr              endp
; ---------------------------------------------------------------------------------------------
CharRead              proc      nLin:DWORD, nCol:DWORD
                             push     ebx

_iCharRead:             invoke    CursorSetPos, nLin, nCol                ; Potition cursor
                             call         KeyWait
                             jnc          short _CharRead1                         ; There is 1 InputRecord         
                             ;
                             pop     ebx
                             ret
                           
_CharRead1:          cmp     ebx, TRUE
                           je         short _CharRead2                        ; key was pressed
                           ;
                           ; key was released -> Clean InputBuffer
                            ; ---------------------------------------------------
                           call        PrintPresRel
                           invoke   FlushConsoleInputBuffer, _hInputBuffer
                            jmp      _iCharRead
                             ;
                             ; Get codes
                             ; ------------
_CharRead2:           mov      ecx, eax
                             invoke  RecordGet                               ; Get codes AL=ascii ...
                             ;
_eCharRead:          clc
                            ;
                            pop     ebx
                            ret
CharRead               endp
; ---------------------------------------------------------------------------------------------
KeyWait                proc
                           LOCAL   nRecRead:DWORD

_iKeyWait:             invoke  ReadConsoleInput, _hInputBuffer,         
                                         ADDR _InputBuffer,       
                                          1,
                                          ADDR nRecRead
                            cmp      eax, 0
                             jne      _KeyWait1                          ; not error         
                  stc
                  ret
                   
_KeyWait1:             mov    ecx, nRecRead                                   ; Number of records read
                             push   esi
                                        mov    esi, offset _InputBuffer
                                        movzx  edx,  word ptr [esi + 0]          ; EventType
                                        mov    ebx, dword ptr [esi + 2]          ; pressed\released
                                        movzx  eax, word ptr  [esi + 6]          ; repeat count
                              pop    esi
;
call      PrintEventTyp                   ; Print Event type
call      PrintRecRead                    ; Print Number of records read
call      PrintNumRep                    ; Print Repeat count
call      PrintPresRel                      ; Print Pressed or Released
                           ;
                           cmp    ecx, 0
                           je       short _iKeyWait                      ; read 0
                           ;
                           cmp    edx, KEY_EVENT
                           jne     short _iKeyWait                      ; not key
                            ;
                            clc
                            ret
KeyWait                endp

Thanks

RuiLoureiro

Hi
      I want to add two things: one is to say to MichaelW that i am sure he is a very good person
      and thanks for his help; the other is the following 6 points:
   
1.    I left an attached file cons18.zip in CAMPUS in my topic «Console Input-Output»;

2.    In the previous post i said that «KeyWait never returns to CharRead»;

3.    I modified CharRead in this way

            cmp     ebx, TRUE
            je      short _CharRead2                           ; key was pressed
            ;
            ; key was released -> Clean InputBuffer
            ; ----------------------------------------------------
            invoke   PASStrPrint, ADDR _MsgReleased1, 3, 40
            invoke   FlushConsoleInputBuffer, _hInputBuffer
            jmp      _iCharRead

where
                      dd 12               
_MsgReleased1 db "ret Released"   


4.    In this case, i see that when i hold down a key, KeyWait returns       
       but EBX = FALSE ( the result is «ret Released» ) !

5.    According to documentation, the DWORD at position +2 in the structure INPUT_RECORD is
       KeyDown DWORD ? ; meaning pressed= TRUE    or   released= FALSE.
      
6.    Then, we can conclude that SOMETHING IS WRONG: in my computer or in the documentation.