I've been trying to improve my technique, and now use my own GUI to display the message to bypass the restrictions on the default MsgBox. Thanks to some clever code by garry, I can now also retrieve the line that threw the error regardless of whether or not the script is compiled (although you probably will not be able to if mpress was used when the script was compiled).
using try/catch:
Code: Select all
;
; AutoHotkey Version: 1.1.30.01
; Language: English
; Platform: Optimized for Windows 10
; Author: Sam.
;
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
#Warn All, StdOut ; Enable warnings to assist with detecting common errors.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
#SingleInstance Force ; Skips the dialog box and replaces the old instance automatically, which is similar in effect to the Reload command.
Global A_Quote:=Chr(34)
OnError("Traceback")
Main()
MsgBox Continue...
ExitApp
Main(){
try {
f()
} catch e {
;ThrowMsg(16,"Error!","Exception thrown!`n`nWhat = " e.what "`nFile = " e.file "`nLine = " e.line "`nMessage = " e.message "`nExtra = " e.extra)
ExceptionErrorDlg(e)
Return
}
}
a() {
b()
}
b() {
c()
}
c() {
;~ file:=FileOpen("filethatdoesnotexist.tmp","r")
throw { what: (IsFunc(A_ThisFunc)?"function: " A_ThisFunc "()":"") A_Space (IsLabel(A_ThisLabel)?"label: " A_ThisLabel:""), file: A_LineFile, line: A_LineNumber, message: "We threw an exception.", extra: "Things went wrong...`n`n" Traceback()}
}
f() {
a()
}
Traceback(exception:="",actual:=0){
i:=0, trace:="", hdr:="Traceback (most recent call last):`n", n:=actual?0:A_AhkVersion<"2"?1:2
Loop {
e:=Exception(".",offset:=-(A_Index+n))
If (e.What=offset)
Break
trace:=" File " A_Quote e.File A_Quote ", line " e.Line " called " e.What "()`n" trace
}
;MsgBox % trace
If IsObject(exception)
{
exception.Extra.="`n`n" hdr trace
ExceptionErrorDlg(exception)
Return 1
}
Return (hdr trace)
}
ExceptionErrorDlg(Content){
Gui, ExcErrDlg:Color, White
If !IsObject(Content) ; Content is plain text
Gui, ExcErrDlg:Add, Text, , %Content%
Else ; Content is an exception object
{
Content.Extra.=" File " A_Quote Content.File A_Quote ", line " Content.Line (Content.What<>":="?" in " Content.What:"") ":`n"
Contents:="Error: " Content.Message "`n`nSpecifically: " Content.Extra "`n`tLine#`n--->`t" GetScriptLine(Content.Line) "`n`nThe current thread will exit."
Gui, ExcErrDlg:Add, Text, , %Contents%
}
Gui, ExcErrDlg:Add, Button,w100 gExcErrDlgGuiClose, OK
Gui, ExcErrDlg:+ToolWindow +AlwaysOnTop
Gui, ExcErrDlg:+HWNDhExcErrDlg
Gui, ExcErrDlg:Show, , Error!
WinWaitClose, % "ahk_id " hExcErrDlg
}
ExcErrDlgGuiClose:
Gui, ExcErrDlg:Destroy
Return
GetScriptLine(LineNum){
If !A_IsCompiled
FileReadLine, Line, %A_LineFile%, LineNum
Else
{
SourceCode:=GetSourceCode()
Loop, Parse, SourceCode, `n, `r
{
If (A_Index=LineNum+1)
{
Line:=A_LoopField
Break
}
}
}
Return LineNum ": " Trim(Line," `t")
}
; EXE2AHK by garry
; https://autohotkey.com/boards/viewtopic.php?f=6&t=59015
GetSourceCode(){
fileObj2:=scanFileForString(A_ScriptFullPath,"; <COMPILER","")
If IsObject(fileObj2)
{
aah:=fileObj2.Read()
fileObj2.Close()
}
Return aah
}
scanFileForString(filePath,searchString,stringEncoding:="UTF-8"){
VarSetCapacity(pBin,StrPut(searchString,stringEncoding)*((stringEncoding="UTF-16"||stringEncoding="cp1200")?2:1),0)
searchBinaryLength:=StrPut(searchString,&pBin,StrLen(searchString),stringEncoding)*((stringEncoding="UTF-16"||stringEncoding="cp1200")?2:1)
Return scanFileForBinary(filePath,pBin,searchBinaryLength,stringEncoding)
}
scanFileForBinary(filePath,ByRef searchBinary,searchBinarylength,FileEncoding:="UTF-8"){
If !FileExist(filePath)
Return
Offset:=0
fileObj:=FileOpen(filePath,"r")
Loop
{
If (fileObj.ReadUChar()=NumGet(searchBinary,Offset,"UChar"))
{
Offset++
If (Offset=searchBinarylength)
{
fileObj.pos-=Offset
Return fileObj
}
}
Else If (offset)
fileObj.pos-=(Offset-1), Offset:=0
}Until fileObj.AtEOF
}
Using OnError:
Code: Select all
;
; AutoHotkey Version: 1.1.30.01
; Language: English
; Platform: Optimized for Windows 10
; Author: Sam.
;
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
#Warn All, StdOut ; Enable warnings to assist with detecting common errors.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
#SingleInstance Force ; Skips the dialog box and replaces the old instance automatically, which is similar in effect to the Reload command.
Global A_Quote:=Chr(34)
OnError("Traceback")
Main()
MsgBox Continue...
ExitApp
Main(){
f()
}
a() {
b()
}
b() {
c()
}
c() {
%cause% := error
}
f() {
a()
}
Traceback(exception:="",actual:=0){
i:=0, trace:="", hdr:="Traceback (most recent call last):`n", n:=actual?0:A_AhkVersion<"2"?1:2
Loop {
e:=Exception(".",offset:=-(A_Index+n))
If (e.What=offset)
Break
trace:=" File " A_Quote e.File A_Quote ", line " e.Line " called " e.What "()`n" trace
}
;MsgBox % trace
If IsObject(exception)
{
exception.Extra.="`n`n" hdr trace
ExceptionErrorDlg(exception)
Return 1
}
Return (hdr trace)
}
ExceptionErrorDlg(Content){
Gui, ExcErrDlg:Color, White
If !IsObject(Content) ; Content is plain text
Gui, ExcErrDlg:Add, Text, , %Content%
Else ; Content is an exception object
{
Content.Extra.=" File " A_Quote Content.File A_Quote ", line " Content.Line (Content.What<>":="?" in " Content.What:"") ":`n"
Contents:="Error: " Content.Message "`n`nSpecifically: " Content.Extra "`n`tLine#`n--->`t" GetScriptLine(Content.Line) "`n`nThe current thread will exit."
Gui, ExcErrDlg:Add, Text, , %Contents%
}
Gui, ExcErrDlg:Add, Button,w100 gExcErrDlgGuiClose, OK
Gui, ExcErrDlg:+ToolWindow +AlwaysOnTop
Gui, ExcErrDlg:+HWNDhExcErrDlg
Gui, ExcErrDlg:Show, , Error!
WinWaitClose, % "ahk_id " hExcErrDlg
}
ExcErrDlgGuiClose:
Gui, ExcErrDlg:Destroy
Return
GetScriptLine(LineNum){
If !A_IsCompiled
FileReadLine, Line, %A_LineFile%, LineNum
Else
{
SourceCode:=GetSourceCode()
Loop, Parse, SourceCode, `n, `r
{
If (A_Index=LineNum+1)
{
Line:=A_LoopField
Break
}
}
}
Return LineNum ": " Trim(Line," `t")
}
; EXE2AHK by garry
; https://autohotkey.com/boards/viewtopic.php?f=6&t=59015
GetSourceCode(){
fileObj2:=scanFileForString(A_ScriptFullPath,"; <COMPILER","")
If IsObject(fileObj2)
{
aah:=fileObj2.Read()
fileObj2.Close()
}
Return aah
}
scanFileForString(filePath,searchString,stringEncoding:="UTF-8"){
VarSetCapacity(pBin,StrPut(searchString,stringEncoding)*((stringEncoding="UTF-16"||stringEncoding="cp1200")?2:1),0)
searchBinaryLength:=StrPut(searchString,&pBin,StrLen(searchString),stringEncoding)*((stringEncoding="UTF-16"||stringEncoding="cp1200")?2:1)
Return scanFileForBinary(filePath,pBin,searchBinaryLength,stringEncoding)
}
scanFileForBinary(filePath,ByRef searchBinary,searchBinarylength,FileEncoding:="UTF-8"){
If !FileExist(filePath)
Return
Offset:=0
fileObj:=FileOpen(filePath,"r")
Loop
{
If (fileObj.ReadUChar()=NumGet(searchBinary,Offset,"UChar"))
{
Offset++
If (Offset=searchBinarylength)
{
fileObj.pos-=Offset
Return fileObj
}
}
Else If (offset)
fileObj.pos-=(Offset-1), Offset:=0
}Until fileObj.AtEOF
}
Still can't run traceback on compiled code, and still can't run traceback after an AHK function or command in a try block has raised an exception caught in a catch block. Any suggestions for improvement are welcome.