News:

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

crt__kbhit deadlock ??

Started by Ficko, July 23, 2009, 08:32:43 PM

Previous topic - Next topic

Ficko

Hello !

I am kind of stuck on this.
Don't know why the second call to "crt__kbhit" deadlocks ? :(

Any idea?

Thanks beforehand!


include masm32rt.inc  
.486                 
option casemap :none   
; #########################################################################
   include msvcrt.inc
     
   includelib user32.lib
   includelib kernel32.lib
   includelib msvcrt.lib
   include \masm32\macros\macros.asm

    WinMain PROTO
.data
        CommandLine   dd 0
        hInstance     dd 0     
.code
start:
    invoke GetModuleHandle, NULL ; provides the instance handle
    mov hInstance, eax
    invoke GetCommandLine        ; provides the command line address
    mov CommandLine, eax
    invoke WinMain
    invoke ExitProcess,eax       ; cleanup & return to operating system
; #########################################################################
WinMain proc
invoke AllocConsole
print "First console",10,13
print "Press <any> key to continue..."
invoke FlushConsoleInputBuffer, rv(GetStdHandle,STD_INPUT_HANDLE)
@@: invoke Sleep, 1
call crt__kbhit
test eax, eax
je @B
call FreeConsole
invoke AllocConsole
print "Second console",10,13
print "Press <any> key to end"
invoke FlushConsoleInputBuffer, rv(GetStdHandle,STD_INPUT_HANDLE)
@@: invoke Sleep, 1
call crt__kbhit
test eax, eax
je @B
ret
WinMain endp
end start

jj2007

A FreeConsole before the first AllocConsole was missing, but apparently it does not solve the problem. I added some error checking...

include \masm32\include\masm32rt.inc

WinMain PROTO

.data
        CommandLine   dd 0
        hInstance     dd 0     
.code
start:
    invoke GetModuleHandle, NULL ; provides the instance handle
    mov hInstance, eax
    invoke GetCommandLine        ; provides the command line address
    mov CommandLine, eax
    invoke WinMain
    invoke ExitProcess,eax       ; cleanup & return to operating system
; #########################################################################
WinMain proc
if 1
invoke FreeConsole ; needed; otherwise, AllocConsole will fail
.if eax==0
print "Free first console FAIL",10,13
.endif
invoke Sleep, 100
endif
invoke AllocConsole
.if eax==0
print "First console FAIL",10,13
.endif
print "First console",10,13
print "Press <any> key to continue..."
invoke FlushConsoleInputBuffer, rv(GetStdHandle,STD_INPUT_HANDLE)
@@: invoke Sleep, 1
call crt__kbhit
test eax, eax
je @B
invoke FreeConsole
.if eax==0
print "Free second console FAIL",10,13
.endif
invoke Sleep, 100
invoke AllocConsole
.if eax==0
print "Second console FAIL",10,13
.endif
print "Second console",10,13
print "Press <any> key to end"
invoke FlushConsoleInputBuffer, rv(GetStdHandle,STD_INPUT_HANDLE)
@@: invoke Sleep, 1
call crt__kbhit
test eax, eax
je @B
ret
WinMain endp
end start

dedndave

just to be sure it is crt__kbhit......

@@:     invoke  Sleep, 1
        call    crt__kbhit
        test    eax, eax
        je      @B
        print   chr$('key pressed'),13,10  ;temporary test code
        ret

that way, if the ret is somehow messed up because of the stack, you will know
(better to use exit or invoke ExitProcess,0)

also, i suggest a sleep time of 100 or something
no need to test for a key every millisecond

MichaelW

It looks like the CRT _kbhit function does not monitor the second console.
eschew obfuscation

dedndave

i was just reading AllocConsole
it says you only get one console

GregL

Maybe this will shed some light on it.  I'm assume you are linking with /SUBSYSTEM:WINDOWS as you have a WinMain.

    INFO: Calling CRT Output Routines from a GUI Application



jj2007

Quote from: Greg on July 24, 2009, 04:09:51 AM
I'm assume you are linking with /SUBSYSTEM:WINDOWS as you have a WinMain.


Greg, I tried both console and windows, same behaviour...

MichaelW

Quote from: dedndave on July 24, 2009, 12:54:52 AM
i was just reading AllocConsole
it says you only get one console

Yes, only one console at a time, but by second console I mean the console allocated after the first is freed.
If I build this as a console app:

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    .data
    .code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    invoke AllocConsole
    print ustr$(eax),13,10

    print "print to original console",13,10
    invoke crt_printf, chr$("crt_printf to original console",13,10)

    invoke FlushConsoleInputBuffer, rv(GetStdHandle,STD_INPUT_HANDLE)
  @@:
    invoke Sleep, 1
    call crt__kbhit
    test eax, eax
    jz @B
    call crt__getch

    invoke FreeConsole
    invoke AllocConsole

    print "print to new console",13,10
    invoke crt_printf, chr$("crt_printf to new console",13,10)

    invoke FlushConsoleInputBuffer, rv(GetStdHandle,STD_INPUT_HANDLE)
  @@:
    invoke Sleep, 1
    call crt__kbhit
    test eax, eax
    jz @B
    call crt__getch

    inkey "Press any key to exit..."
    exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start


Then the first call to AllocConsole fails (returns zero) and everything works except crt__kbhit in the second console. But if I build it with just assemble and link, then the first call to AllocConsole succeeds and print works, but the only CRT component that works is crt__kbhit in the first console.

eschew obfuscation

dedndave

i think the inkey macro uses both crt__kbhit and crt__getch
in fact, the loop you are using looks very similar to the macro
anyways, try Sleep,100 in the loops - it seems to matter (if i remember that's what the macro uses)

MichaelW

What I used was copied directly from the inkey macro, including the Sleep 1.

BTW, the resolution for Sleep is normally 10ms.




eschew obfuscation

evlncrn8

kinda obvious, but could it possibly be that your handles are destroyed when you do the freeconsole
then when you do the allocconsole, you need to do the GetStdHandle api's to get the new handle?

dedndave

#11
i wrote a function similar to crt__kbhit/crt__getch
it always empties the buffer on exit
the FlushConsoleInputBuffer could be removed from the routine, but i was trying to fix a different kind of bug
this is something i have been working on for a while - lol

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    .data
    .data?
hStdInp dd ?
EventCount dd ?
InpEvntRec INPUT_RECORD <>
    .code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    invoke AllocConsole
    print ustr$(eax),13,10

    print "print to original console",13,10
    invoke crt_printf, chr$("crt_printf to original console",13,10)
    invoke GetStdHandle,STD_INPUT_HANDLE
    mov hStdInp,eax
    invoke FlushConsoleInputBuffer,eax
  @@:
    invoke Sleep, 1
    call crt__kbhit
    test eax, eax
    jz @B
    call crt__getch

    invoke FreeConsole
    invoke AllocConsole

    print "print to new console",13,10
    invoke crt_printf, chr$("crt_printf to new console",13,10)
    invoke GetStdHandle,STD_INPUT_HANDLE
    mov hStdInp,eax
    invoke FlushConsoleInputBuffer,eax

print chr$('Press any key to close this console')
test00:
invoke Sleep,100
call InKyb
jz test00
exit

;--------------------------------------------------------------------------

InKyb   PROC

;Poll keyboard

InKyb0: INVOKE  PeekConsoleInput,
                hStdInp,
                addr InpEvntRec,
                1,
                addr EventCount
        mov     eax,EventCount
        or      eax,eax
        jz      InKyb1             ;ZF set if no key

        INVOKE  ReadConsoleInput,
                hStdInp,
                addr InpEvntRec,
                1,
                addr EventCount
        movzx   eax,word ptr InpEvntRec.EventType
        dec     eax
        jnz     InKyb0

        dec dword ptr InpEvntRec.KeyEvent.bKeyDown
        jnz     InKyb0

        INVOKE  FlushConsoleInputBuffer,
                hStdInp
        xor     eax,eax
        inc     eax                ;ZF cleared if key
        movzx   eax,word ptr InpEvntRec.KeyEvent.wVirtualScanCode

InKyb1: ret

InKyb   ENDP

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start

EDIT
you may want to "hide" the cursor during the inkey loop
i usually turn it off at the beginning of the program and turn it back on just before exit

        .data?
hStdOut dd ?
CursInfo CONSOLE_CURSOR_INFO <>

        .code

;save initial cursor visibility value
;set cursor visibility to false (off)

        INVOKE  GetStdHandle,
                STD_OUTPUT_HANDLE
        mov     hStdOut,eax
        INVOKE  GetConsoleCursorInfo,
                eax,
                ADDR CursInfo
        push    CursInfo.bVisible
        mov dword ptr CursInfo.bVisible,FALSE
        INVOKE  SetConsoleCursorInfo,
                hStdOut,
                ADDR CursInfo
        pop     CursInfo.bVisible

;set cursor visibility to initial setting

        INVOKE  SetConsoleCursorInfo,
                hStdOut,
                ADDR CursInfo

Ficko

Thanks guys !

I have learned a lot. :bg

I come up with a "home-made" kbhit.

It seems to work. :8)

- It even won't distruct "crt__kbhit" in the second console. -


include \masm32\include\masm32rt.inc

WinMain PROTO
.data
        CommandLine   dd 0
        hInstance     dd 0
.code
start:
    invoke GetModuleHandle, NULL ; provides the instance handle
    mov hInstance, eax
    invoke GetCommandLine        ; provides the command line address
    mov CommandLine, eax
    invoke WinMain
    invoke ExitProcess,eax       ; cleanup & return to operating system
; #########################################################################
WinMain proc
;invoke FreeConsole ; needed only if compiled as console
LOCAL lpBuffer :INPUT_RECORD
LOCAL hConsoleInput :DWORD
LOCAL lpNumberOfEventsRead :DWORD
.if eax==0
print "Free first console FAIL",10,13
.endif
invoke Sleep, 100
invoke AllocConsole
.if eax==0
print "First console FAIL",10,13
.endif
invoke GetStdHandle, STD_INPUT_HANDLE
mov hConsoleInput, eax
invoke FlushConsoleInputBuffer, hConsoleInput
print "First console",10,13
print "Press <any> key to continue...",10,13
call kbhit
invoke FreeConsole
.if eax==0
print "Free second console FAIL",10,13
.endif
invoke Sleep, 100
invoke AllocConsole
.if eax==0
print "Second console FAIL",10,13
.endif
invoke FlushConsoleInputBuffer, rv(GetStdHandle,STD_INPUT_HANDLE)
print "Second console",10,13
print "Press <any> key to end"
@@: invoke Sleep, 1
call crt__kbhit
test eax, eax
je @B
ret
; ########### kbhit PROC ########### 
kbhit: .repeat
invoke PeekConsoleInput, hConsoleInput,ADDR lpBuffer,1,ADDR lpNumberOfEventsRead
.until (lpNumberOfEventsRead != 0)
.if (lpBuffer.EventType != KEY_EVENT) || (lpBuffer.KeyEvent.bKeyDown == 0)
invoke FlushConsoleInputBuffer, hConsoleInput
jmp kbhit
.endif
retn
WinMain endp
end start


dedndave

i try to write routines that replace the crt functions
they are great for development, but if i write code to be released, i prefer no crt
crt functions are large and probably require some type of licensing statement
some of the masm32 library macros use crt functions - inkey is one of them

Vortex

Hi Dave,

There is no need to specify any licensing statement to use msvcrt functions.