Jump to content

Recommended Posts

Posted (edited)

Debug-console version of AutoIt3.exe and AutoIt3_x64.exe?


I want to test my script (it already passes Au3Check.exe) by running it through CMD,

and I want any errors (crashes, syntax errors, array index issues, invalid assignments, etc.) to be printed directly to the console instead of stopping the program and showing an error message box.
Those pop-up error dialogs make debugging difficult because I have to switch back to SciTE every time.

What I’m asking for is a console-mode debug version of AutoIt3.exe/AutoIt3_x64.exe that, when an error occurs, prints the error to the console and exits immediately with a non-zero exit code.

 

And with compiled scripts too, is it possible to print a message on console instead of showing it?

Edited by Trong

Enjoy my work? Buy me a 🍻 or tip via ❤️ PayPal

Posted

SciTE don't get those popups. They run with "/ErrorStdOut."
Then you also have Opt("SetExitCode",1) 1 = Set @exitCode on Fatal error - see Exitcodes.

If that is not enough then, let me know.

P.S.: I really like the open source code you're integrating. Like really really like it. Thanks for that 💯

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Posted
#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Icon=Icons\au3script_v9.ico
#AutoIt3Wrapper_Outfile=Autoit3cui.exe
#AutoIt3Wrapper_Outfile_x64=AutoIt3_x64cui.exe
#AutoIt3Wrapper_Compile_Both=y
#AutoIt3Wrapper_UseX64=y
#AutoIt3Wrapper_Change2CUI=y
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
#cs ----------------------------------------------------------------------------

 AutoIt Version: 3.3.16.1
 Author:         myName

#ce ----------------------------------------------------------------------------

; Script Start - Add your code below here

FileChangeDir(@ScriptDir) ; my AutoIt is installed there

Opt("TrayAutoPause", 0) ; Script pauses when click on tray icon = OFF/0
Opt("TrayOnEventMode", 0) ; Enable/disable OnEvent functions notifications for the tray = OFF/0
Opt("GUICloseOnESC", 1) ; When ESC is pressed on a GUI the $GUI_EVENT_CLOSE message is sent = ON/1

#pragma compile(Console, True)
#pragma compile(AutoItExecuteAllowed, True)

..also used the above and instead of running a script with AutoIt3.exe, I would run it with Autoit3cui.exe
If these cui versions are in the same folder as AutoIt3.exe, all will be running just fine.

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Posted

It seems not to be the optimal solution.
Even running with /ErrorStdOut still does not show the error on the console, only when running with SCITE !!

Error.png

Does anyone know how to use this command?  /ErrorStdOut

Enjoy my work? Buy me a 🍻 or tip via ❤️ PayPal

Posted (edited)

I had a similar problem a long time ago. A server application started my script. In rare cases, the script crashed due to faulty data.
Trancexx helped me with this code.

Edited by water

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2024-07-28 - Version 1.6.3.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

  • Developers
Posted

Or make a simply RunConsole.au3 script and compile it:

#AutoIt3Wrapper_Change2CUI=y
$pgm = '"C:\Program Files (x86)\AutoIt3\AutoIt3.exe" /ErrorStdOut "' & $CMDLineRAW & '"'
_RunCMDPgm($pgm)

Func _RunCMDPgm($pgm)
    Local $pid
    $pid = Run($pgm, '', @SW_HIDE, 1 + 2 + 4)
    Local $handle = _ProcessExitCode($pid)
    ShowStdOutErr($pid)
    Local $exitcode = _ProcessExitCode($pid, $handle)
    _ProcessCloseHandle($handle)
    ConsoleWrite("+ Ended  rc:" & $exitcode & @LF)
    SetError($exitcode)
EndFunc   ;==>_RunCMDPgm
;
Func _ProcessExitCode($i_Pid, $h_Process = 0)
    ; 0 = Return Process Handle of PID else use Handle to Return Exitcode of a PID
    Local $v_Placeholder
    If Not IsArray($h_Process) Then
        ; Return the process handle of a PID
        $h_Process = DllCall('kernel32.dll', 'ptr', 'OpenProcess', 'int', 0x400, 'int', 0, 'int', $i_Pid)
        If Not @error Then Return $h_Process
    Else
        ; Return Process Exitcode of PID
        $h_Process = DllCall('kernel32.dll', 'ptr', 'GetExitCodeProcess', 'ptr', $h_Process[0], 'int*', $v_Placeholder)
        If Not @error Then Return $h_Process[2]
    EndIf
    Return 0
EndFunc   ;==>_ProcessExitCode
;
Func _ProcessCloseHandle($h_Process)
    ; Close the process handle of a PID
    DllCall('kernel32.dll', 'ptr', 'CloseHandle', 'ptr', $h_Process)
    If Not @error Then Return 1
    Return 0
EndFunc   ;==>_ProcessCloseHandle
;
Func ShowStdOutErr($l_Handle)
    Local $Line, $tot_out, $err1 = 0, $err2 = 0
    Do
        Sleep(10)
        $Line = StdoutRead($l_Handle)
        $err1 = @error
        ConsoleWrite($Line)
        $Lineerr = StderrRead($l_Handle)
        $err2 = @error
        ConsoleWrite($Lineerr)
    Until $err1 And $err2
    Return
EndFunc   ;==>ShowStdOutErr

Then run the script from the console with this compiled program:

C:\test>RunConsole.exe "C:\test\test.au3"
$CMDLineRAW=/ErrorStdOut ""C:\test\test.au3""
"C:\test\test.au3" (6) : ==> Array variable has incorrect number of subscripts or subscript dimension range exceeded.:
$x [10] = 9
^ ERROR
+ Ended  rc:1

.. and bob's your uncle. 😉

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

Posted (edited)

Hi @Trong.

The issue is the way AutoIt3.exe does outputs from it's runtime
My suggestion is based on this: https://stackoverflow.com/a/54252275

"D:\Downloads\autoit-v3.3.16.1\install\AutoIt3.exe" /ErrorStdOut "D:\Downloads\autoit-v3.3.16.1\install\test.au3" 2>&1|more

this was my test script:

ConsoleWrite("ConsoleWriteTest"&@crlf)
Local $x[1]
$x[10] = 9
MsgBox(0, "aya", "yay")

and here is the result:

image.thumb.png.9c47ced967e99145573a508f1d14c00b.png

Edited by genius257
Posted

AutoIt3cui:

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Icon=C:\Program Files (x86)\AutoIt3\Icons\MyAutoIt3_Blue.ico
#AutoIt3Wrapper_Outfile=AutoIt3cui.exe
#AutoIt3Wrapper_Outfile_x64=AutoIt3cui_x64.exe
#AutoIt3Wrapper_Compile_Both=y
#AutoIt3Wrapper_UseX64=y
#AutoIt3Wrapper_Change2CUI=y
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Configuration
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Opt("TrayAutoPause", 0)
Opt("GUICloseOnESC", 0)

Global Const $STR_STRIPLEADING = 1
Global Const $STR_STRIPTRAILING = 2
Global Const $PROCESS_QUERY_INFORMATION = 0x0400
Global Const $PAGE_READWRITE = 0x04

; Hook MessageBox if compiled
If @Compiled Then _AddHookApi("user32.dll", "MessageBoxW", "_Intercept_MessageBoxW", "int", "hwnd;wstr;wstr;uint")

; Main execution
Global $sAutoItPath = _GetAutoItPath()
Global $sCMDLineRAW = _CleanCommandLine($CMDLineRAW)
Global $iTimeInit = TimerInit()

; Exit early if no arguments
If StringStripWS($sCMDLineRAW, 7) == '' Then
    ConsoleWrite('! The process will quit immediately since no command-line arguments are present.' & @CRLF)
    Exit 0
EndIf

; Log execution
ConsoleWrite(StringFormat("+ [%02d:%02d:%02d] %s CMDLine: %s%s", @HOUR, @MIN, @SEC, @ScriptName, $sCMDLineRAW, @CRLF))

; Build and execute command
;~ Global $sCommand = '"' & $sAutoItPath & '" /ErrorStdOut ' & $sCMDLineRAW & ' 2>&1|more'
Global $sCommand = '"' & $sAutoItPath & '" /ErrorStdOut ' & $sCMDLineRAW & ''
Exit _ExecuteCommand($sCommand)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Core Functions - Optimized
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Func _CleanCommandLine($sCmd)
    ; Remove common AutoIt paths and flags using array for efficiency
    Local $aRemove[] = [ _
            '"C:\Program Files (x86)\AutoIt3\AutoIt3.exe"', _
            '"C:\Program Files (x86)\AutoIt3\SciTE\..\AutoIt3.exe"', _
            '"C:\Program Files (x86)\AutoIt3\AutoIt3_x64.exe"', _
            '"C:\Program Files (x86)\AutoIt3\SciTE\..\AutoIt3_x64.exe"', _
            'C:\Program Files (x86)\AutoIt3\AutoIt3.exe', _
            'C:\Program Files (x86)\AutoIt3\AutoIt3_x64.exe', _
            '/ErrorStdOut', _
            '"' & $sAutoItPath & '"', _
            $sAutoItPath, _
            '"' & @ScriptFullPath & '"', _
            @ScriptFullPath _
            ]

    For $sPattern In $aRemove
        $sCmd = StringReplace($sCmd, $sPattern, '')
    Next

    ; Normalize whitespace
    $sCmd = StringRegExpReplace($sCmd, '[\r\n]+', ' ')  ; Replace line breaks
    $sCmd = StringRegExpReplace($sCmd, '\s{2,}', ' ')   ; Collapse multiple spaces
    $sCmd = StringStripWS($sCmd, $STR_STRIPLEADING + $STR_STRIPTRAILING)

    Return $sCmd
EndFunc   ;==>_CleanCommandLine

Func _GetAutoItPath()
    Local $sExeName = @AutoItX64 ? 'AutoIt3_x64.exe' : 'AutoIt3.exe'
    Local $aDirs[] = [ _
            _WinAPI_ExpandEnvironmentStrings('%ProgramFiles%') & '\AutoIt3\', _
            _WinAPI_ExpandEnvironmentStrings('%ProgramFiles(x86)%') & '\AutoIt3\', _
            @ScriptDir & '\' _
            ]

    For $sDir In $aDirs
        If FileExists($sDir & $sExeName) Then Return $sDir & $sExeName
    Next

    ; Fallback
    Return @ScriptDir & '\' & $sExeName
EndFunc   ;==>_GetAutoItPath

Func _ExecuteCommand($sCommand)
    ; Start process with stdout/stderr redirection
    Local $iPID = Run($sCommand, '', @SW_HIDE, BitOR(0x1, 0x2, 0x4)) ; STDIN, STDOUT, STDERR
    If @error Or Not $iPID Then
        ConsoleWrite('! Error: Failed to start process.' & @CRLF)
        Return SetError(1, 0, 1)
    EndIf

    ; Get process handle for exit code
    Local $hProcess = _ProcessOpenHandle($iPID)
    If @error Then
        ConsoleWrite('! Warning: Failed to open process handle.' & @CRLF)
    EndIf

    ; Stream output (outputs are prefixed in child process)
    _StreamProcessOutput($iPID)

    ; Get exit code
    Local $iExitCode = _ProcessGetExitCode($iPID, $hProcess)
    _ProcessCloseHandle($hProcess)

    ; Log result
    Local $sTimeDiff = _FormatTimeSmart(TimerDiff($iTimeInit))
    Local $sStatus = ($iExitCode == 0) ? '+' : '!'
    Local $sResult = ($iExitCode == 0) ? 'completed successfully' : 'failed'

    ConsoleWrite(StringFormat('%s [%02d:%02d:%02d] %s %s in %s. Exit code: %d%s', _
            $sStatus, @HOUR, @MIN, @SEC, @ScriptName, $sResult, $sTimeDiff, $iExitCode, @LF))

    Return SetError($iExitCode, 0, $iExitCode)
EndFunc   ;==>_ExecuteCommand

Func _StreamProcessOutput($iPID)
    ; Optimized output streaming with smaller buffer reads
    Local $sStdOut, $sStdErr
    Local $bStdOutDone = False, $bStdErrDone = False

    While Not ($bStdOutDone And $bStdErrDone)
        ; Read stdout
        $sStdOut = StdoutRead($iPID)
        If @error Then
            $bStdOutDone = True
        ElseIf $sStdOut Then
            ConsoleWrite($sStdOut)
        EndIf

        ; Read stderr
        $sStdErr = StderrRead($iPID)
        If @error Then
            $bStdErrDone = True
        ElseIf $sStdErr Then
            ConsoleWrite($sStdErr)
        EndIf

        ; Small sleep to prevent CPU spinning (reduced from 5ms to 1ms)
        If Not ($bStdOutDone And $bStdErrDone) Then Sleep(1)
    WEnd
EndFunc   ;==>_StreamProcessOutput

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Time & Process Helpers
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Func _FormatTimeSmart($fMS)
    If $fMS < 1000 Then Return Round($fMS) & "ms"

    Local $iHours = Floor($fMS / 3600000)
    $fMS = Mod($fMS, 3600000)
    Local $iMinutes = Floor($fMS / 60000)
    $fMS = Mod($fMS, 60000)
    Local $iSeconds = Floor($fMS / 1000)
    Local $iMillis = Mod($fMS, 1000)

    Return StringFormat("%02d:%02d:%02d:%03d", $iHours, $iMinutes, $iSeconds, $iMillis)
EndFunc   ;==>_FormatTimeSmart

Func _ProcessOpenHandle($iPID)
    Local $aCall = DllCall('kernel32.dll', 'ptr', 'OpenProcess', 'dword', $PROCESS_QUERY_INFORMATION, 'bool', False, 'dword', $iPID)
    If @error Or Not $aCall[0] Then Return SetError(1, 0, 0)
    Return $aCall[0]
EndFunc   ;==>_ProcessOpenHandle

Func _ProcessGetExitCode($iPID, $hProcess)
    If Not $hProcess Then Return 0

    Local $aCall = DllCall('kernel32.dll', 'bool', 'GetExitCodeProcess', 'ptr', $hProcess, 'dword*', 0)
    If @error Or Not $aCall[0] Then Return 0
    Return $aCall[2]
EndFunc   ;==>_ProcessGetExitCode

Func _ProcessCloseHandle($hProcess)
    If Not $hProcess Then Return 0
    DllCall('kernel32.dll', 'bool', 'CloseHandle', 'ptr', $hProcess)
    Return Not @error
EndFunc   ;==>_ProcessCloseHandle

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; API Hooking Functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Func _AddHookApi($sModuleName, $vFunctionName, $vNewFunction, $sRet = "", $sParams = "")
    Local Static $pImportDirectory, $hInstance

    ; Initialize on first call
    If Not $pImportDirectory Then
        $hInstance = _GetModuleHandle()
        If @error Then Return SetError(1, 0, 0)

        Local $aCall = DllCall("dbghelp.dll", "ptr", "ImageDirectoryEntryToData", _
                "handle", $hInstance, "boolean", True, "word", 1, "dword*", 0)
        If @error Or Not $aCall[0] Then Return SetError(2, 0, 0)

        $pImportDirectory = $aCall[0]
    EndIf

    Local $bIsOrdinal = IsInt($vFunctionName)
    Local $bRestore = Not IsString($vNewFunction)
    Local $pDirectoryOffset = $pImportDirectory

    ; Iterate through import directory
    While True
        Local $tImport = DllStructCreate("dword RVAOriginalFirstThunk;" & _
                "dword TimeDateStamp;dword ForwarderChain;" & _
                "dword RVAModuleName;dword RVAFirstThunk", $pDirectoryOffset)

        If Not DllStructGetData($tImport, "RVAFirstThunk") Then ExitLoop

        Local $tModName = DllStructCreate("char Name[64]", $hInstance + DllStructGetData($tImport, "RVAModuleName"))

        If DllStructGetData($tModName, "Name") == $sModuleName Then
            Local $pThunk = $hInstance + DllStructGetData($tImport, "RVAFirstThunk")
            Local $pOrigThunk = $hInstance + DllStructGetData($tImport, "RVAOriginalFirstThunk")
            If $pOrigThunk == $hInstance Then $pOrigThunk = $pThunk

            Local $iOffset = 0
            While True
                Local $tPtr = DllStructCreate("ptr", $pOrigThunk + $iOffset)
                Local $pValue = DllStructGetData($tPtr, 1)
                If Not $pValue Then ExitLoop

                Local $bMatch = False
                If $bIsOrdinal Then
                    $bMatch = (BitAND($pValue, 0xFFFFFF) == $vFunctionName)
                Else
                    Local $tName = DllStructCreate("ushort Ordinal;char Name[64]", $hInstance + $pValue)
                    $bMatch = (DllStructGetData($tName, "Name") == $vFunctionName)
                EndIf

                If $bMatch Then
                    Local $tFunc = DllStructCreate("ptr", $pThunk + $iOffset)
                    If Not _VirtualProtect(DllStructGetPtr($tFunc), DllStructGetSize($tFunc), $PAGE_READWRITE) Then
                        Return SetError(3, 0, 0)
                    EndIf

                    Local $pOld = DllStructGetData($tFunc, 1)
                    Local $pNew = $bRestore ? $vNewFunction : DllCallbackGetPtr(DllCallbackRegister($vNewFunction, $sRet, $sParams))
                    DllStructSetData($tFunc, 1, $pNew)

                    Return $pOld
                EndIf

                $iOffset += DllStructGetSize($tPtr)
            WEnd
            ExitLoop
        EndIf

        $pDirectoryOffset += 20
    WEnd

    Return SetError(4, 0, 0)
EndFunc   ;==>_AddHookApi

Func _VirtualProtect($pAddress, $iSize, $iProtection)
    Local $aCall = DllCall("kernel32.dll", "bool", "VirtualProtect", _
            "ptr", $pAddress, "dword_ptr", $iSize, "dword", $iProtection, "dword*", 0)
    If @error Or Not $aCall[0] Then Return SetError(1, 0, 0)
    Return True
EndFunc   ;==>_VirtualProtect

Func _GetModuleHandle($vModule = 0)
    Local $sType = IsString($vModule) ? "wstr" : "ptr"
    Local $aCall = DllCall("kernel32.dll", "ptr", "GetModuleHandleW", $sType, $vModule)
    If @error Or Not $aCall[0] Then Return SetError(1, 0, 0)
    Return $aCall[0]
EndFunc   ;==>_GetModuleHandle

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; String & WinAPI Helpers
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Func _WinAPI_GetString($pString, $bUnicode = True)
    Local $iLen = _WinAPI_StrLen($pString, $bUnicode)
    If @error Or Not $iLen Then Return SetError(@error + 10, @extended, '')

    Local $sType = $bUnicode ? 'wchar' : 'char'
    Local $tString = DllStructCreate($sType & '[' & ($iLen + 1) & ']', $pString)
    If @error Then Return SetError(@error, @extended, '')

    Return SetExtended($iLen, DllStructGetData($tString, 1))
EndFunc   ;==>_WinAPI_GetString

Func _WinAPI_StrLen($pString, $bUnicode = True)
    Local $sFunc = $bUnicode ? 'lstrlenW' : 'lstrlenA'
    Local $aCall = DllCall('kernel32.dll', 'int', $sFunc, 'struct*', $pString)
    If @error Then Return SetError(@error, @extended, 0)
    Return $aCall[0]
EndFunc   ;==>_WinAPI_StrLen

Func _WinAPI_ExpandEnvironmentStrings($sString)
    Local $aCall = DllCall("kernel32.dll", "dword", "ExpandEnvironmentStringsW", _
            "wstr", $sString, "wstr", "", "dword", 4096)
    If @error Or Not $aCall[0] Then Return SetError(@error + 10, @extended, "")
    Return $aCall[2]
EndFunc   ;==>_WinAPI_ExpandEnvironmentStrings

 

Enjoy my work? Buy me a 🍻 or tip via ❤️ PayPal

Posted

ok, my bad I guess ( because I didn't test with errors as my code is always perfect :P )
AutoItCui.bat:

@Echo OFF
:: AutoItCui.bat ; name of the batch file that runs Autoit3cui
Autoit3cui /ErrorStdOut /AutoIt3ExecuteScript %*
echo rc:%errorlevel%

With the above batch file you'd run your script. Say:

Opt("SetExitCode",1)
Global $a[1]
$a[1] = 1

and the output would be:

>Autoitcui MyFlaw.au3
"D:\Utilities\AutoIt3\MyFlaw.au3" (3) : ==> Array variable has incorrect number of subscripts or subscript dimension range exceeded.:
$a[1] = 1
^ ERROR
rc:2147479674

were the 2147479674 errorlevel in hex is 0x7FFFF07A.

So the code I posted in the beginning is all that is really needed.

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

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
  • Recently Browsing   0 members

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