Jump to content

Console.au3 UDF


Shaggi
 Share

Recommended Posts

Sup,

This isn't really an advanced UDF, but implements a couple of functions and features, with the idea of substituting the original functions like ConsoleWrite() and ConsoleWriteError() and the seriously sketchy ConsoleRead() lol.

Public functions:

  • Cout()
  • Cin()
  • Getch()
  • Cerr()
  • system()
  • RegisterConsoleEvent()
The motive for writing this was the missing ability to read user input from consoles in AutoIt, known as std::cin >> in C++. The UDF's design allows quick and simple access to the functions, without startup / shutdown and handle passing - all this is automatically taken care of. Some users may have know, that you cannot open a console if the script is running from SciTE - this UDF handles that problem, so you can open an console while running scripts through SciTE.

Features:

  • Dynamically opens and closes consoles
  • Use the STDOUT, STDERR and STDIN streams like in other languages.
  • Print coloured/styled text
  • Access the command interpreter through the console.
  • Uses unicode.
  • Register console events.
  • The UDF is designed to work standalone and to have minimal overhead (> 0.1 ms for writing a coloured string)
The UDF supports my style of scripting. I hate long names so standard UDF naming like _Console_WriteConsoleW() is abbreviated to Cout() for now. I also hate having to start up certain UDF's by calling funcs etc., so this is handled first time you call a function. Handles are controlled globally and closed when needed, so you don't need to pass them around lol.

Why would anyone need this?

Good question. I guess this mainly goes out to people with a bit of nostalgia in them. I personally like the feel of a console.

Picture:

Posted Image

Changelog:

  • 25-06-2012

    • Fixed system() to allow case sensitive text.
  • 25-06-2012

    • Fixed typo's.
    • Changed frequent dllcalls to use dllcalladdress for speed.
    • Added RegisterConsoleEvent - functions can be registrered to be called on certain events, like closure of console. This can be used to mimic OnAutoItExit behaviour, that wont be called in case of bad closure.
    • Various optimizations.
  • 09-07-2011

    • Added Getch() function.
    • Better error handling all around.
    • Put back color attribute constants as they are more symbolic imo.
    • Decreased the standard size of Cin allocation (bit harsh to allocate 2 kb memory each time) - it's now changed to 128 chars.
  • 05-06-2011

    • Added color constants thanks to Warmonger.
  • 16-03-2011

    • Fixed Cin() bug.
    • Cin() isn't returning a trailing CRLF anymore.
    • Fixed startup function, so it should run from SciTE now.
  • 15-03-2011 - Initial release.
Notes

I know Matt Diesel was/is working on large, similar UDF, however i wanted something simpler and easier to use. This is the result. Thoughts? Posted Image

Console.au3

Edited by Shaggi

Ever wanted to call functions in another process? ProcessCall UDFConsole stuff: Console UDFC Preprocessor for AutoIt OMG

Link to comment
Share on other sites

Not bad. I think I'll have to improve my Console UDFs, too :)

*GERMAN* [note: you are not allowed to remove author / modified info from my UDFs]My UDFs:[_SetImageBinaryToCtrl] [_TaskDialog] [AutoItObject] [Animated GIF (GDI+)] [ClipPut for Image] [FreeImage] [GDI32 UDFs] [GDIPlus Progressbar] [Hotkey-Selector] [Multiline Inputbox] [MySQL without ODBC] [RichEdit UDFs] [SpeechAPI Example] [WinHTTP]UDFs included in AutoIt: FTP_Ex (as FTPEx), _WinAPI_SetLayeredWindowAttributes

Link to comment
Share on other sites

Looks very interesting. Any example available?

Br,

UEZ

Thanks and yes, well this is a pretty common example:

#include <Console.au3>

Main()

Func Main()
    Local $Name
    Local $Age
    Local $Answer
    Local $Continue = True

    While $Continue
        Cout("Enter your name: ")
        Cin($Name)
        Cout("Enter your age: ")
        Cin($Age)
        Cout("Do you want your answers printed in red? y/n: ")
        Cin($Answer)
        If StringInStr($Answer,"y") Then
            Cout(@LF & "Your name is ")
            Cout($Name & @LF,$FOREGROUND_RED)
            Cout("You were born in ")
            Cout(@Year - $Age & @LF,$FOREGROUND_RED)
        Else
            Cout(@LF & "Your name is ")
            Cout($Name & @LF)
            Cout("You were born in ")
            Cout(@Year - $Age & @LF)
        EndIf
        $Answer = ""
        Cout(@LF & "Do you want to try again? y/n: ")
        Cin($Answer)
        If StringInStr($Answer,"n") Then
            $Continue = False
        EndIf
    WEnd

    system("pause")
EndFunc

Not bad. I think I'll have to improve my Console UDFs, too :)

Thanks. :) Edited by Shaggi

Ever wanted to call functions in another process? ProcessCall UDFConsole stuff: Console UDFC Preprocessor for AutoIt OMG

Link to comment
Share on other sites

Thanks for sharing Shaggi.

One suggestion: I had an error regarding writting memory, but when I modified it (see below), it worked.

In your Cin function, add this (see the <<<<<<<<<<< sign):

Local $aResult = DllCall($__Dll_Kernel32, "BOOL", "ReadConsoleW", _
            "handle", $__ConsoleHandle__Input, _
            "ptr", DllStructGetPtr($lpBuffer), _
            "dword", DllStructGetSize($lpBuffer), _
            "dword*", $lpNumberOfCharsRead, _
            "ptr", 0) ;<<<<<<<<<<< ADD THIS

taietel

Link to comment
Share on other sites

Thanks for the example. As taietel said it is crashing but working when I add the line mentioned by him.

Br,

UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

You are right that it is a lot easier to use than mine, as mine is a direct wrapper around the WinAPI. Maybe I'll implement similar functions when I finish my UDF.

It still doesn't work running from SciTE (mine has the same problem). Any idea how to fix that?

Well currently mine does (by me, anyway - anyone having trouble with new version?). It opens a new instance of the script around SciTE but still does the checking etc.

As for that last parameter, it should be optional (according to MSDN):

BOOL WINAPI ReadConsole(
  __in      HANDLE hConsoleInput,
  __out     LPVOID lpBuffer,
  __in      DWORD nNumberOfCharsToRead,
  __out     LPDWORD lpNumberOfCharsRead,
  __in_opt  LPVOID pInputControl
);
But i added it anyway in the new release...

Ever wanted to call functions in another process? ProcessCall UDFConsole stuff: Console UDFC Preprocessor for AutoIt OMG

Link to comment
Share on other sites

You should do the command line properly. ShellExecuteWait is not a good solution because (as is the case with me) it doesn't always work.

Edit: It works when I use:

Local $iReturnCode = RunWait(@AutoItExe & ' ' & StringStripWS(StringTrimLeft($CmdLineRaw, StringLen("/ErrorStdOut")), 0x1 + 0x2))
Edited by Mat
Link to comment
Share on other sites

As for that last parameter, it should be optional (according to MSDN):

Optional means, that it is allowed to be NULL. AFAIK, this parameter has a default value of NULL in the C-headers, so it is not required to add it to the actual function call, but in AutoIt you have to use the complete call.

*GERMAN* [note: you are not allowed to remove author / modified info from my UDFs]My UDFs:[_SetImageBinaryToCtrl] [_TaskDialog] [AutoItObject] [Animated GIF (GDI+)] [ClipPut for Image] [FreeImage] [GDI32 UDFs] [GDIPlus Progressbar] [Hotkey-Selector] [Multiline Inputbox] [MySQL without ODBC] [RichEdit UDFs] [SpeechAPI Example] [WinHTTP]UDFs included in AutoIt: FTP_Ex (as FTPEx), _WinAPI_SetLayeredWindowAttributes

Link to comment
Share on other sites

Optional means, that it is allowed to be NULL. AFAIK, this parameter has a default value of NULL in the C-headers, so it is not required to add it to the actual function call, but in AutoIt you have to use the complete call.

I see :) I haven't experienced a crash yet though... Maybe it depends on os, or length of the input string?

You should do the command line properly. ShellExecuteWait is not a good solution because (as is the case with me) it doesn't always work.

Edit: It works when I use:

Local $iReturnCode = RunWait(@AutoItExe & ' ' & StringStripWS(StringTrimLeft($CmdLineRaw, StringLen("/ErrorStdOut")), 0x1 + 0x2))

Isn't this fixed in the new version? Try redownloading.

Ever wanted to call functions in another process? ProcessCall UDFConsole stuff: Console UDFC Preprocessor for AutoIt OMG

Link to comment
Share on other sites

Yes sorry, it is fixed. There must be a better way to do it than to spawn a new process though...

Apparently not.. hm.

Looks very nice,

can it be used for "expect like" scripting?

running commands in console and wait for output.

it can save me using TKL and use only autoit.

Thanks.

Well, something like this?

#include <Console.au3>

_Main()

Func _Main()
    Local $CommandString, $Answer, $Continue = True
    While $Continue
        Cin($CommandString)
        Cout(Execute($CommandString) & @CRLF,$BACKGROUND_GREEN)
        Cout(@LF & "Do you want to try again? y/n: ")
        Cin($answer)
        If $Answer == "n" Then
            $Continue = False
        EndIf
    WEnd
    system("pause")
EndFunc

Please elaborate, i'm not quite sure i understand you.

Ever wanted to call functions in another process? ProcessCall UDFConsole stuff: Console UDFC Preprocessor for AutoIt OMG

Link to comment
Share on other sites

Here is some code I use in the development-version of my console.au3 :) With this, the console should be displayed when running from scite.

If Not @Compiled And StringRegExp($CMDLINERAW, "(?i)(\A|\s)/ErrorStdOut(\s|\Z)") Then
        Local $aTmp[3][2] = [["CONIN$", 2],["CONOUT$", 4],["CONERR$", 4]]
        For $i = 0 To 2
            $GLOBAL_hConsole[$i] = _WinAPI_CreateFile($aTmp[$i][0], 2, $aTmp[$i][1], $aTmp[$i][1])
            If $GLOBAL_hConsole[$i] = 0 Then
                If $ExitOnFatal Then _WinAPI_FatalAppExit("GetStdHandle for " & $msg[$i] & " failed")
                Return SetError(2 + $i, 0, 0)
            EndIf
        Next
    Else
        For $i = 0 To 2
            $GLOBAL_hConsole[$i] = _WinAPI_GetStdHandle($i)
            If $GLOBAL_hConsole[$i] <= 0 Then
                If $ExitOnFatal Then _WinAPI_FatalAppExit("GetStdHandle for " & $msg[$i] & " failed")
                Return SetError(2 + $i, 0, 0)
            EndIf
        Next
    EndIf

*GERMAN* [note: you are not allowed to remove author / modified info from my UDFs]My UDFs:[_SetImageBinaryToCtrl] [_TaskDialog] [AutoItObject] [Animated GIF (GDI+)] [ClipPut for Image] [FreeImage] [GDI32 UDFs] [GDIPlus Progressbar] [Hotkey-Selector] [Multiline Inputbox] [MySQL without ODBC] [RichEdit UDFs] [SpeechAPI Example] [WinHTTP]UDFs included in AutoIt: FTP_Ex (as FTPEx), _WinAPI_SetLayeredWindowAttributes

Link to comment
Share on other sites

Apparently not.. hm.

Well, something like this?

#include <Console.au3>

_Main()

Func _Main()
    Local $CommandString, $Answer, $Continue = True
    While $Continue
        Cin($CommandString)
        Cout(Execute($CommandString) & @CRLF,$BACKGROUND_GREEN)
        Cout(@LF & "Do you want to try again? y/n: ")
        Cin($answer)
        If $Answer == "n" Then
            $Continue = False
        EndIf
    WEnd
    system("pause")
EndFunc

Please elaborate, i'm not quite sure i understand you.

Thanks, but i think its not what im looking for,

i will try to explain:

my goal is to open cmd window and run command into it, wait for the output and then run the next command, and so on.

the flow should look something like:

run cmd ->

send to console - format x:

wait for output - "Proceed with Format (Y/N)?"

send to console - "N"

wait for output - "c:\" (the prompt)

I want to use it to automate ssh connections, the format is just example... :)

Link to comment
Share on other sites

Thanks, but i think its not what im looking for,

i will try to explain:

my goal is to open cmd window and run command into it, wait for the output and then run the next command, and so on.

the flow should look something like:

run cmd ->

send to console - format x:

wait for output - "Proceed with Format (Y/N)?"

send to console - "N"

wait for output - "c:\" (the prompt)

I want to use it to automate ssh connections, the format is just example... :)

Yes, that is possible. However since the system() command blocks, you can't use it like you probably would...

#include <Console.au3>

__main()

Func __Main()
    Local $CommandString, $Answer, $Continue = True, $iReturn
    While $Continue
        Cout(@CRLF & "Enter your command: " & @CRLF,$FOREGROUND_RED)
        Cin($CommandString)
        $iReturn = system($commandstring)
        Cout(@LF & "Return from system: ")
        Cout(" " & $iReturn & @CRLF,$BACKGROUND_GREEN)
        Cout(@LF & "Do you want to try again? y/n: ")
        Cin($answer)
        If $Answer == "n" Then
            $Continue = False
        EndIf
    WEnd
    system("pause")
EndFunc

Try something like this instead:

$iHandle = Run("cmd.exe",@ScriptDIr,@SW_HIDE,0x1 + 0x8)
Global $lastErr

While 1
    $Command = GetCommand()
    If $LastErr THen ExitLoop
    Dispatch($Command)
    PrintOutput()
Wend

StdIoClose($iHandle)

Func GetCommand()
    Local $ans = InputBox("Command Interpreter","Please enter a command...")
    $LastErr = @Error
    Return $Ans
EndFunc

Func Dispatch($szCommand)
    StdinWrite($iHandle,$szCommand & @CRLF)
EndFunc

Func PrintOutput()
    $nMsg = StdoutRead($iHandle)
    Local $hasBeenRead = False, $Count = 0
    While NOT @error
        IF $hasBeenRead AND $nMsg == "" Then
            $Count += 1
            Sleep(100)
            If $Count > 10 Then ExitLoop
        EndIf
        ConsoleWrite($nMsg)
        $nMsg = StdoutRead($iHandle)
        If $nMsg <> "" Then $hasBeenRead = True
        If @Error Then ExitLoop
    WEnd
EndFunc

Ever wanted to call functions in another process? ProcessCall UDFConsole stuff: Console UDFC Preprocessor for AutoIt OMG

Link to comment
Share on other sites

Plugging in your console UDF seemed like a easy way to get colored output in the console I'm using ... but that was a short exercise.

(My exe being a CUI thats using the real windows-dos console. Oops, small communication mismatch on the word 'Console'.)

Still a nice UDF though.

"Straight_and_Crooked_Thinking" : A "classic guide to ferreting out untruths, half-truths, and other distortions of facts in political and social discussions."
"The Secrets of Quantum Physics" : New and excellent 2 part documentary on Quantum Physics by Jim Al-Khalili. (Dec 2014)

"Believing what you know ain't so" ...

Knock Knock ...
 

Link to comment
Share on other sites

  • 1 month later...

Here's a update to this UDF (Shaggi I hope you don't mind just minor things). You can now use all 255 color combinations in a console, however you cannot Obfuscate your source no longer (bug).

Removed:

$FOREGROUND_X
$BACKGROUND_X

Added:

$COLOR_X (1-255)

Posted Image

#include <Console.au3>

Dim $COLOR_

For $i = 1 to 255
    Cout($i & ' ', $COLOR_ & $i)
    Sleep(10)
Next

While 1 = 1
    Sleep(1000)
WEnd

Console.au3

Edited by Warmonger
Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...