Page 1 of 1

Only catch errors causing the script to exit

Posted: 22 Jul 2016, 15:50
by boiler
I would like to use try over a large block of code (a whole script, really), but I don't want it to catch ErrorLevel-type runtime errors. I want to only catch those that would cause the script to stop executing. Alternatively, if it does catch an ErrorLevel-type error, I would like to have it continue executing on the line following the one that caused the ErrorLevel. I can determine in the catch block whether the error was an ErrorLevel type or not, but I can't get it to continue where it left off once I've determined that. Is that possible?

In the following demonstration/test code, I'd like it to display only the MsgBox saying "Show me."

Code: Select all

try
{
	ImageSearch, x, y, 0, 0,100,100, image.png
	MsgBox, Show me.
	obj := ComObjCreate("ScriptControl")
    obj.ExecuteStatement("MsgBox ""This is embedded VBScript""")
	obj.InvalidMethod() ; This line produces a runtime error.
	MsgBox, Don't show me.
}
catch e
{
	ErrorMessage := e.message
	if ErrorMessage is not integer
	{
		MsgBox, % "A non ErrorLevel error has occurred.`n`n" e.message "`n`nExiting script."
		ExitApp
	}
	MsgBox, % "ErrorLevel: " e.message
}

Re: Only catch errors causing the script to exit

Posted: 22 Jul 2016, 21:15
by lexikos
It is not possible to "continue where it left off", because execution has already exited every block/function/context between the point the exception was thrown and the try statement. If it has exited functions, local variables have already been cleared.

There is currently no way to prevent try from changing the behaviour of ErrorLevel-type errors, except to use AutoHotkey v2-alpha instead of v1.

However,
  • You can catch the error dialog which normally appears and replace it with something else, as shown below.
  • A debugger client (a program or script utilizing the DBGp protocol to debug the script) can "redirect stderr", which causes all error messages to be sent to the debugger client instead of being shown to the user. (I only remembered this while trying to test the script below, as debugging in SciTE4AutoHotkey was preventing the script from working.)
The following is an updated version of a script I originally posted on the old forum.

Code: Select all

SuppressRuntimeErrors("Error at line {#}.  Please contact support.")
MsgBox % %empty%  ; Normally "This dynamic variable is blank."
return

SuppressRuntimeErrors(NewErrorFormat)
{
	; Call self-contained helper/message-monitor function:
	return SuppressRuntimeErrors_(NewErrorFormat, 0, 0, 0)
}

SuppressRuntimeErrors_(wParam, lParam, msg, hwnd)
{
	; Constants:
	static WM_COMMNOTIFY := 0x0044, AHK_DIALOG := 1027
	; Persistent variables:
	static sScriptPID := 0, sMessage
	
	Critical 1000
	
	if hwnd     ; Called internally to handle a WM_COMMNOTIFY message.
	{
		DetectHiddenWindows On
		
		if (hwnd = A_ScriptHwnd  ; Script's main window is the recipient.
			&& wParam = AHK_DIALOG  ; We're showing a dialog of some sort.
			&& WinExist("ahk_class #32770 ahk_pid " sScriptPID))
		{
			ControlGetText msg, Static1
			; The following relies on the fact that all built-in error
			; dialogs use this format to point out the current line:
			static RegEx := (A_AhkVersion>="2" ? "" : "O") "m`a)^--->`t0*\K\d+(?=:)"
			if RegExMatch(msg, RegEx, line)
			{
				; If we change the text, the dialog will still be sized
				; based on the previous text.  So instead, close this
				; dialog and show a new one.
				DllCall("DestroyWindow", "ptr", WinExist())
				msg := StrReplace(sMessage, "{#}", line.0)
				MsgBox 48,, %msg%
				
				ExitApp
				; If not exiting, just indicate this message has been handled:
				; return 0
			}
		}
	}
	else        ; Called by script.
	{
		sMessage := wParam
		
		; If we're already registered, just return.
		if sScriptPID
			return
		
		; Retrieve script's process ID.
		sScriptPID := DllCall("GetCurrentProcessId", "uint")
		
		; Register message handler.  Since hotkeys and other things can
		; launch new threads while we're displaying our error dialog,
		; pass 10 for MaxThreads so that we can catch any error dialogs
		; that these other threads might display:
		OnMessage(WM_COMMNOTIFY, Func(A_ThisFunc), 10)
	}
}
This version has been tested on AutoHotkey v1.1.24.00 and v2.0-a075.

Re: Only catch errors causing the script to exit

Posted: 23 Jul 2016, 10:09
by boiler
I see. Thanks very much for updating that script. That will be quite useful. I will probably want to change the RegEx needle to capture a different part of the error text, such as everything before the "--->" or certain parts of it.

Eventually, I will start using AutoHotkey v2, but I haven't made the jump yet.