Jump to content
Vlad_D

FileOpen("con") not working

Recommended Posts

Vlad_D

I am trying (and failing) to read a key pressed by the user in a console application.

I have tried the following code (from the "Console Input" post):

#AutoIt3Wrapper_Change2CUI=y

$input = ""
ConsoleWrite(@CRLF & "Write some text and close with ENTER: ")

$file = FileOpen("con", 4)
While 1
    $chr = FileRead($file, 1)
    If $chr = @LF Then ExitLoop
    $input = $input & BinaryToString($chr)
WEnd
FileClose($file)

ConsoleWrite(@CRLF & "Input was: " & $input & @CRLF)

I compiled it for CUI and started it in a CMD box. It doesn't read anything ... I have to use ^C to exit (and all the typed characters show on the next command prompt). I tried to debug it using $FO_BINARY (16) instead of the undocumented 4, but I have got the same result. I instrumented the code a bit and found out that each call to FileRead sets an @error value of -1, indicated that the stream from "con" reached its EOF. So while this way of reading the user's input seemed to work fine in 2009, it is surely broken now.

I am using AutoIT v3.3.14.2 on a Windows Server 2003 PC. Can anyone help?

Also, digging some more on the web, I found a reference indicating possible alternatives using _getch(), ReadConsole() or ReadConsoleInput(). Does anyone have a tested example of how to use any of those?

Share this post


Link to post
Share on other sites
Vlad_D
4 minutes ago, Jos said:

Are you using the Full SciTE4AutoIt3 package that uses AutoIt3Wrapper when compiling or else this will not work!

Jos

Yes, I'm pretty sure I'm using it. "About Scite" states: 

SciTE
Version 3.7.3 
    Feb 16 2017 21:41:17
by Neil Hodgson.
 Updated by Jos

Share this post


Link to post
Share on other sites
Vlad_D
27 minutes ago, Jos said:

Are you using the Full SciTE4AutoIt3 package that uses AutoIt3Wrapper when compiling or else this will not work!

Jos

>"D:\WIN52S0X\AutoIt3\SciTE\..\AutoIt3.exe" "D:\WIN52S0X\AutoIt3\SciTE\AutoIt3Wrapper\AutoIt3Wrapper.au3" /NoStatus /prod /in "S:\WinSetup\SysUtils\WIN10\AutoIT3.Dev\Test_009.au3"
+>15:51:06 Starting AutoIt3Wrapper v.17.224.935.0 SciTE v.3.7.3.0   Keyboard:00000409  OS:WIN_2003/Service Pack 2  CPU:X64 OS:X86  Environment(Language:0409)  CodePage:0  utf8.auto.check:4
+>         SciTEDir => D:\WIN52S0X\AutoIt3\SciTE   UserDir => D:\WIN52S0X\_USERS\Odin\Local Settings\Application Data\AutoIt v3\SciTE\AutoIt3Wrapper   SCITE_USERHOME => D:\WIN52S0X\_USERS\Odin\Local Settings\Application Data\AutoIt v3\SciTE 
>Running AU3Check (3.3.14.2)  from:D:\WIN52S0X\AutoIt3  input:S:\WinSetup\SysUtils\WIN10\AutoIT3.Dev\Test_009.au3
+>15:51:06 AU3Check ended.rc:0
>Running:(3.3.14.2):D:\WIN52S0X\AutoIt3\aut2exe\aut2exe.exe  /in "S:\WinSetup\SysUtils\WIN10\AutoIT3.Dev\Test_009.au3" /out "D:\WIN52S0X\_USERS\Odin\Local Settings\Application Data\AutoIt v3\Aut2exe\~AU172E.tmp.exe" /nopack /comp 2 /Console
+>15:51:07 Aut2exe.exe ended.D:\WIN52S0X\_USERS\Odin\Local Settings\Application Data\AutoIt v3\Aut2exe\~AU172E.tmp.exe. rc:0
+>15:51:07 Created program:S:\WinSetup\SysUtils\WIN10\AutoIT3.Dev\Test_009.exe
+>15:51:07 AutoIt3Wrapper Finished.
>Exit code: 0    Time: 1.568

 

Share this post


Link to post
Share on other sites
Jos

That looks correct. Try this script as things probably changed since then:

#AutoIt3Wrapper_Change2CUI=y

$input = ""
ConsoleWrite(@CRLF & "Write some text and close with ENTER: ")

Local $sOutput
While True
    $sOutput &= ConsoleRead()
    If @error Then ExitLoop
    if StringInStr($sOutput,@LF) then ExitLoop
    Sleep(10)
WEnd
MsgBox(0, "", "Received: " & @CRLF & @CRLF & $sOutput)

Jos


SciTE4AutoIt3 Full installer Download page   - Beta files       Read before posting     How to post scriptsource        Forum Rules
 
Live for the present,
Dream of the future,
Learn from the past.
  :)

Share this post


Link to post
Share on other sites
Vlad_D
5 minutes ago, Jos said:

That looks correct. Try this script as things probably changed since then:

#AutoIt3Wrapper_Change2CUI=y

$input = ""
ConsoleWrite(@CRLF & "Write some text and close with ENTER: ")

Local $sOutput
While True
    $sOutput &= ConsoleRead()
    If @error Then ExitLoop
    if StringInStr($sOutput,@LF) then ExitLoop
    Sleep(10)
WEnd
MsgBox(0, "", "Received: " & @CRLF & @CRLF & $sOutput)

Jos

I have tried it ... it prints the text but then shows the MsgBox with an empty output and exits without waiting for the answer. With a bit of code debugging, it seems that the loop is exited because @error is 1, which is probably consistent with an EOF on the console input stream.

Share this post


Link to post
Share on other sites
Vlad_D
4 minutes ago, Jos said:

It works fine for me when I open a CMD window and then start the compiled script.

Jos

On what kind of Windows are you testing?

Share this post


Link to post
Share on other sites
Vlad_D
19 minutes ago, Jos said:

OS:WIN_10/  CPU:X64 OS:X64 

Jos

So I tried it in some virtual machines:

Win 10 x32: It worked as intended.

Win 7 Pro x32: It didn't work (same behavior and error as on my Win 2003).

So it seems there is some backwards compatibility issue ...

I'm back at using the _WinAPI_ReadFile() method from the post I mentioned earlier ... do you have any "proper" example of how to use the SetConsoleMode DllCall (I would like to disable the ENABLE_LINE_INPUT console flag).

Share this post


Link to post
Share on other sites
BrewManNH
29 minutes ago, Vlad_D said:

I have tried it ... it prints the text but then shows the MsgBox with an empty output and exits without waiting for the answer.

Compile the script first.


If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Share this post


Link to post
Share on other sites
Vlad_D
16 minutes ago, BrewManNH said:

Compile the script first.

I sure did that in all my tests ...

Share this post


Link to post
Share on other sites
Vlad_D
2 hours ago, Vlad_D said:

So I tried it in some virtual machines:

Win 10 x32: It worked as intended.

Win 7 Pro x32: It didn't work (same behavior and error as on my Win 2003).

So it seems there is some backwards compatibility issue ...

I'm back at using the _WinAPI_ReadFile() method from the post I mentioned earlier ... do you have any "proper" example of how to use the SetConsoleMode DllCall (I would like to disable the ENABLE_LINE_INPUT console flag).

Just an update, after some more testing:

1. I wasn't able to get the SetConsoleMode DllCall to work. I have tried it as:

;...
$hConsole = _WinAPI_CreateFile("CON", 2, 2)
;...
DllCall("kernel32.dll", "bool", "SetConsoleMode", "handle", $hConsole, "dword", 0)
;...

I didn't get any error, but no result either (in both Win 2003 and Win 10). So if I've got it wrong, please tell me.

2. The _WinAPI_ReadFile() method mentioned above works fine-ish in Win 2003 (I couldn't un-set the ENABLE_LINE_INPUT console flag), and kind of works in Win10: I get the prompt, I type some text, I press Enter, it waits for some more text, I type another text, press Enter and it picks up the second text. I could find some comments (and a potential solution) on a similar topic in a post from Microsoft ... more experimenting to do ... but I'm getting very close to go for a plain C solution.

Share this post


Link to post
Share on other sites
Vlad_D

After a lot of experiments, I think I solved the issue (at least to my satisfaction). Here are a few relevant experiments:

1. As I mentioned in my initial comment, I started from the "Console Input" post. After a few edits, I've got to the point where it was reading a string terminated by [Enter] in Windows 2003/XP, but it was behaving weird in Windows 10 x32:

#include <WinAPI.au3>

Local $nChars, $tBuffer, $hConIN, $nCount, $sInChr, $sInput

$nChars = 1
$tBuffer = DllStructCreate("char")
$hConIN = _WinAPI_CreateFile("CON", 2, 2)
ConsoleWrite(@CRLF & "Write some text and close with ENTER: ")
While 1 ; We read 1 char at a time and we want all of them
  _WinAPI_ReadFile($hConIN, DllStructGetPtr($tBuffer), 1, $nCount)
  If @error Then ExitLoop
  If $nCount > 0 Then
    $sInChr = BinaryToString(DllStructGetData($tBuffer, 1))
    If $sInChr = @CR Then ExitLoop
    $sInput &= $sInChr
  EndIf
WEnd
If @error Then ConsoleWriteError(@CRLF & "ERROR: " & @error)
_WinAPI_CloseHandle($hConIN)

ConsoleWrite(@CRLF & "INPUT: """ & $sInput & """" & @CRLF)

#cs ---------------------------------------------------------------------------
    Windows 2003/XP: Reads string terminated by [Enter]
                   +-----------------------------------------------------------
                   | Write some text and close with ENTER: ABCDE
                   |
                   | INPUT: "ABCDE"
                   +-----------------------------------------------------------
    Windows 10  x32: Reads only the SECOND string terminated by [Enter]
                   +-----------------------------------------------------------
                   | Write some text and close with ENTER: ABCDE
                   | FGHIJ
                   |
                   | INPUT: "FGHIJ"
                   +-----------------------------------------------------------
#ce ---------------------------------------------------------------------------

2. Then I found a way to make this work properly in both environments. The trick was FreeConsole/AttachConsole:

#include <WinAPI.au3>

Local $nChars, $tBuffer, $hConIN, $nCount, $sInChr, $sInput

; The next two lines make ReadFile work well in Windows 10 (and Windows 8.x ?)
DllCall("Kernel32.dll", "bool", "FreeConsole")  ; _WinAPI_FreeConsole() ???
_WinAPI_AttachConsole()

$nChars = 1
$tBuffer = DllStructCreate("char")
$hConIN = _WinAPI_CreateFile("CON", 2, 2)
ConsoleWrite(@CRLF & "Write some text and close with ENTER: ")
While 1 ; We read 1 char at a time and we want all of them
  _WinAPI_ReadFile($hConIN, DllStructGetPtr($tBuffer), $nChars, $nCount)
  If @error Then ExitLoop
  If $nCount > 0 Then
    $sInChr = BinaryToString(DllStructGetData($tBuffer, 1))
    If $sInChr = @CR Then ExitLoop
    $sInput &= $sInChr
  EndIf
WEnd
If @error Then ConsoleWriteError(@CRLF & "ERROR: " & @error)
_WinAPI_CloseHandle($hConIN)

ConsoleWrite(@CRLF & "INPUT: """ & $sInput & """" & @CRLF)

#cs ---------------------------------------------------------------------------
    Windows 2003/XP: Reads string terminated by [Enter]
                   +-----------------------------------------------------------
                   | Write some text and close with ENTER: ABCDE
                   |
                   | INPUT: "ABCDE"
                   +-----------------------------------------------------------
    Windows 10  x32: Reads string terminated by [Enter]
                   +-----------------------------------------------------------
                   | Write some text and close with ENTER: ABCDE
                   |
                   | INPUT: "ABCDE"
                   +-----------------------------------------------------------
#ce ---------------------------------------------------------------------------

3. Then I simplified the code for the situation where I know the maximum number of characters I'm interested in:

#include <WinAPI.au3>

Local $nChars, $tBuffer, $hConIN, $nCount, $sInput

; The next two lines make ReadFile work well in Windows 10 (and Windows 8.x ?)
DllCall("Kernel32.dll", "bool", "FreeConsole")  ; _WinAPI_FreeConsole() ???
_WinAPI_AttachConsole()

$nChars = 3
; The struct size has to be $nChars or larger
$tBuffer = DllStructCreate("char[" & $nChars & "]")
$hConIN = _WinAPI_CreateFile("CON", 2, 2)
ConsoleWrite(@CRLF & "Write some text and close with ENTER: ")
; Read $nChars (or less) at a time
_WinAPI_ReadFile($hConIN, DllStructGetPtr($tBuffer), $nChars, $nCount)
If @error Then ConsoleWriteError(@CRLF & "ERROR: " & @error)
_WinAPI_CloseHandle($hConIN)

$sInput = BinaryToString(DllStructGetData($tBuffer, 1))
; This line is for input strings shorter by 1 compared to $nChars
$sInput = StringReplace($sInput, @CR, "")
; This line is for input strings shorter by 2 or more compared to $nChars
$sInput = StringReplace($sInput, @LF, "")
ConsoleWrite(@CRLF & "INPUT: """ & $sInput & """" & @CRLF)

#cs ---------------------------------------------------------------------------
    Windows 2003/XP: Reads string terminated by [Enter]
                   +-----------------------------------------------------------
                   | Write some text and close with ENTER: ABCDE
                   |
                   | INPUT: "ABC"
                   +-----------------------------------------------------------
                   | Write some text and close with ENTER: AB
                   |
                   | INPUT: "AB"
                   +-----------------------------------------------------------
                   | Write some text and close with ENTER: A
                   |
                   | INPUT: "A"
                   +-----------------------------------------------------------
                   | Write some text and close with ENTER:
                   |
                   | INPUT: ""
                   +-----------------------------------------------------------
    Windows 10  x32: Reads string terminated by [Enter]
                   +-----------------------------------------------------------
                   | Write some text and close with ENTER: ABCDE
                   |
                   | INPUT: "ABC"
                   +-----------------------------------------------------------
                   | Write some text and close with ENTER: AB
                   |
                   | INPUT: "AB"
                   +-----------------------------------------------------------
                   | Write some text and close with ENTER: A
                   |
                   | INPUT: "A"
                   +-----------------------------------------------------------
                   | Write some text and close with ENTER:
                   |
                   | INPUT: ""
                   +-----------------------------------------------------------
#ce ---------------------------------------------------------------------------

4. Then I cracked the issue of having to press [Enter] to trigger the read (I wanted a "Press any key ..." input). The trick was finding the right flags combination for SetConsoleMode:

#include <WinAPI.au3>

Local $aDllRC, $hStdIN, $tBuffer, $hConIN, $nCount, $sInChr

; The next two lines make ReadFile work well in Windows 10 (and Windows 8.x ?)
DllCall("Kernel32.dll", "bool", "FreeConsole")  ; _WinAPI_FreeConsole() ???
_WinAPI_AttachConsole()

; SetConsoleMode is used to get 1 character without having to press [Enter]
Local Const $STD_INPUT =  -10
$aDllRC = DllCall("kernel32.dll", "handle", "GetStdHandle", "dword", $STD_INPUT)
$hStdIN = $aDllRC[0]
; According to Microsoft documentation of SetConsoleMode for Input handles:
;   ENABLE_ECHO_INPUT:              0x0004 - Disabled (For "Press any key: ")
;   ENABLE_EXTENDED_FLAGS:          0x0080 - Enabled (by default)
;   ENABLE_INSERT_MODE:             0x0020 - Enabled (by default)
;   ENABLE_LINE_INPUT:              0x0002 - Disabled (For "Press any key: ")
;   ENABLE_MOUSE_INPUT:             0x0010 - Enabled (by default)
;   ENABLE_PROCESSED_INPUT:         0x0001 - Enabled (by default)
;   ENABLE_QUICK_EDIT_MODE:         0x0040 - Enabled (by default)
;   ENABLE_WINDOW_INPUT:            0x0008 - Disabled (by default)
;   ENABLE_VIRTUAL_TERMINAL_INPUT:  0x0200 - Disabled (For "Press any key: ")
;                           TOTAL:  0x00F1 - Enabled only
DllCall("kernel32.dll", "bool", "SetConsoleMode", "handle", $hStdIN, "dword", 0x00F1)

$tBuffer = DllStructCreate("char")
$hConIN = _WinAPI_CreateFile("CON", 2, 2)
ConsoleWrite(@CRLF & "Press any key: ")
; Read just 1 character
_WinAPI_ReadFile($hConIN, DllStructGetPtr($tBuffer), 1, $nCount)
If @error Then ConsoleWriteError(@CRLF & "ERROR: " & @error)
_WinAPI_CloseHandle($hConIN)

$sInChr = BinaryToString(DllStructGetData($tBuffer, 1))
; This line is for when the key pressed is [Enter]
$sInChr = StringReplace($sInChr, @CR, "")
ConsoleWrite(@CRLF & "INPUT: """ & $sInChr & """" & @CRLF)

#cs ---------------------------------------------------------------------------
    Windows 2003/XP: Reads string terminated by [Enter]
                   +-----------------------------------------------------------
                   | Press any key:
                   | INPUT: "A"
                   +-----------------------------------------------------------
                   +-----------------------------------------------------------
                   | Press any key:
                   | INPUT: ""
                   +-----------------------------------------------------------
    Windows 10  x32: Reads string terminated by [Enter]
                   +-----------------------------------------------------------
                   | Press any key:
                   | INPUT: "A"
                   +-----------------------------------------------------------
                   +-----------------------------------------------------------
                   | Press any key:
                   | INPUT: ""
                   +-----------------------------------------------------------
#ce ---------------------------------------------------------------------------

And this made me really happy ... :drool: 

5. However, I wanted to evaluate the suggestion made earlier by Jos, which is way simpler and works on Windows 10 but not on 2003/XP:

Local $sInput

ConsoleWrite(@CRLF & "Write some text and close with ENTER: ")
While True  ; ConsoleRead needs to be polled ...
  $sInput &= ConsoleRead()
  If @error Then ExitLoop
  if $sInput <> "" then ExitLoop
  Sleep(10)
WEnd
If @error Then ConsoleWriteError(@CRLF & "ERROR: " & @error)

$sInput = StringReplace($sInput, @CRLF, "")
ConsoleWrite(@CRLF & "INPUT: """ & $sInput & """" & @CRLF)

#cs ---------------------------------------------------------------------------
    Windows 2003/XP: Skips the reading (ConsoleRead returns @error=1)
                   +-----------------------------------------------------------
                   | Write some text and close with ENTER:
                   | ERROR: 1
                   | INPUT: ""
                   +-----------------------------------------------------------
    Windows 10  x32: Reads string terminated by [Enter] (and keeps @CRLF)
                   +-----------------------------------------------------------
                   | Write some text and close with ENTER: ABCDE
                   |
                   | INPUT: "ABCDE"
                   +-----------------------------------------------------------
#ce ---------------------------------------------------------------------------

6. To my total surprise, the same two lines of code that made the previous solution work well, have totally broken this one:

Local $sInput

; The next two lines BREAK ConsoleRead in Windows 10 (and Windows 8.x ?)
DllCall("Kernel32.dll", "bool", "FreeConsole")
DllCall("kernel32.dll", "bool", "AttachConsole", "dword", -1)

ConsoleWrite(@CRLF & "Write some text and close with ENTER: ")
While True  ; ConsoleRead needs to be polled ...
  $sInput &= ConsoleRead()
  If @error Then ExitLoop
  if $sInput <> "" then ExitLoop
  Sleep(10)
WEnd
If @error Then ConsoleWriteError(@CRLF & "ERROR: " & @error)

$sInput = StringReplace($sInput, @CRLF, "")
ConsoleWrite(@CRLF & "INPUT: """ & $sInput & """" & @CRLF)

#cs ---------------------------------------------------------------------------
    Windows 2003/XP: Skips the reading (ConsoleRead returns @error=1)
                   +-----------------------------------------------------------
                   | Write some text and close with ENTER:
                   | ERROR: 1
                   | INPUT: ""
                   +-----------------------------------------------------------
    Windows 10  x32: Skips the reading (ConsoleRead returns @error=1)
                   +-----------------------------------------------------------
                   | Write some text and close with ENTER:
                   | ERROR: 1
                   | INPUT: ""
                   +-----------------------------------------------------------
#ce ---------------------------------------------------------------------------

Food for thought ... :evil: ... but I'm done with this.

Share this post


Link to post
Share on other sites
Vlad_D

Just for completeness, re-initializing the console also helps Windows 2003/XP:

#AutoIt3Wrapper_Change2CUI=y

#include <WinAPI.au3>

Local $hConsole, $aResults

; GetConsoleMode before re-initializing the console
$hConsole = _WinAPI_GetStdHandle(0)
$aResults = DllCall("kernel32.dll", "BOOL", "GetConsoleMode", "handle", $hConsole, "DWORD*", 0)
ConsoleWrite("OLD console - Mode: " & Hex($aResults[2]) & @CRLF)

; Re-initialize the console
DllCall("Kernel32.dll", "bool", "FreeConsole")
_WinAPI_AttachConsole()

; GetConsoleMode after re-initializing the console
$hConsole = _WinAPI_GetStdHandle(0)
$aResults = DllCall("kernel32.dll", "BOOL", "GetConsoleMode", "handle", $hConsole, "DWORD*", 0)
ConsoleWrite("NEW console - Mode: " & Hex($aResults[2]) & @CRLF)

#cs ---------------------------------------------------------------------------
    Windows 2003/XP: Makes GetConsoleMode() work properly
                   +-----------------------------------------------------------
                   | OLD console - Mode: 0000000000000000
                   | NEW console - Mode: 00000000000000E7
                   +-----------------------------------------------------------
#ce ---------------------------------------------------------------------------

Also, in the example #4, above, setting a console mode of 0x00F0 (instead of 0x00F1) allows the "capture" of Ctrl+C as well (that means not setting the ENABLE_PROCESSED_INPUT flag).

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×