Exception Handling Directive
Posted: 27 May 2015, 09:15
I've grown to rely more and more on AutoHotkey's exception handling capabilities (and less and less on "msgbox, something went wrong!"), and it suits most purposes well. However, I find its functionality lacking in one key area: there is currently no documented way to capture and handle all runtime exceptions. This doesn't become really necessary until you have a large and complex script with multiple possible threads, since in a simple procedural script you could just throw everything in a huge try/catch clause. I explored the forums and managed to stump a couple folks on the IRC without finding a way to do it, but if I've overlooked something, please let me know! I'd essentially like to override or tack on to the default behavior of displaying a dialogue of the exception's Message, What, and Extra. Here's my use case:
I've designed a big, clunky gui with buttons and other controls full of "risky operations"...code that is capable of throwing an exception either specifically designed or baked in at a lower level.
Now I need to ship this script out to 50 end users who are less than fully computer literate. A few of them complain that they're seeing errors popping up, but they can't remember what they were doing when it happened or what the error message was. After a week of pleading with them to slow down and preserve the error information for me to troubleshoot, I decide that I'm going to implement a function to catch all exceptions and email their information to the helpdesk as a new ticket. How should I go about this?
Well, that didn't work. Those try/catch statements are never actually encountered in the flow of execution once a new thread is launched at any of those gui labels. Proper design obligates me to be aware of when my code is capable of throwing an exception and to address each of these instances individually. This is not impractical in this simplistic example, of course, though even on this scale it does look tedious.
But what if there is a potential exception that I am truly unaware of? I'm stuck running back and forth between users' desks trying to reproduce it since I don't know where it is. My lovely helpdesk email function can only notify me of exceptions I already knew were possible.
What I envision is a directive similar to OnMessage...Somewhere I can specify a function to run any time an exception goes uncaught.
Any thoughts? Is such an implementation possible?
I've designed a big, clunky gui with buttons and other controls full of "risky operations"...code that is capable of throwing an exception either specifically designed or baked in at a lower level.
Code: Select all
Gui, New
Gui, Add, Button, gButton1 vButton1 w200, Button 1
Gui, Add, Button, gButton2 vButton2 w200, Button 2
Gui, Add, Button, gButton3 vButton3 w200, Button 3
Gui, Add, Button, gButton4 vButton4 w200, Button 4
Gui, Add, Button, gButton5 vButton5 w200, Button 5
Gui, Show
return
Button1:
Throw, Exception("You pressed " A_GuiControl, 0, "Why would you do that?!")
return
Button2:
Throw, Exception("I might have expected this exception.", 0, "Because I designed it myself.")
return
Button3:
risky_operation()
return
Button4:
borrowed_function()
return
Button5:
msgBox % "There is nothing wrong with clicking this button."
return
Guiclose:
GuiEscape:
ExitApp
risky_operation()
{
Random, random_number, 0, 1
if random_number
{
Throw, Exception("You knew this was a posibility!")
}
else
{
msgBox % "Nothing to see here."
}
}
borrowed_function()
{
; ...
; code formatted according to someone else's standards
; ...
Throw, Exception("Hah! Gotcha.")
; ...
}
Code: Select all
; ...
Try
{
Button1:
Throw, Exception("You pressed " A_GuiControl, 0, "Why would you do that?!")
return
Button2:
Throw, Exception("I might have expected this exception.", 0, "Because I designed it myself.")
return
Button3:
risky_operation()
return
Button4:
borrowed_function()
return
Button5:
msgBox % "There is nothing wrong with clicking this button."
return
}
Catch e
{
; email it to the helpdesk
msgBox % "I'm so clever."
}
; ...
Code: Select all
; ...
Button1:
Try
{
Throw, Exception("You pressed " A_GuiControl, 0, "Why would you do that?!")
}
Catch e
{
email_helpesk_exception(e)
}
return
Button2:
Try
{
Throw, Exception("I might have expected this exception.", 0, "Because I designed it myself.")
}
Catch e
{
email_helpesk_exception(e)
}
return
Button3:
Try
{
risky_operation()
}
Catch e
{
email_helpesk_exception(e)
}
return
Button4:
Try
{
borrowed_function()
}
Catch e
{
email_helpesk_exception(e)
}
return
Button5:
msgBox % "There is nothing wrong with clicking this button."
return
; ...
What I envision is a directive similar to OnMessage...Somewhere I can specify a function to run any time an exception goes uncaught.
Code: Select all
OnException("Email_Helpdesk_Exception")
Gui, New
Gui, Add, Button, gButton1 vButton1 w200, Button 1
Gui, Add, Button, gButton2 vButton2 w200, Button 2
Gui, Add, Button, gButton3 vButton3 w200, Button 3
Gui, Add, Button, gButton4 vButton4 w200, Button 4
Gui, Add, Button, gButton5 vButton5 w200, Button 5
Gui, Show
return
Button1:
Throw, Exception("You pressed " A_GuiControl, 0, "Why would you do that?!")
return
Button2:
Throw, Exception("I might have expected this exception.", 0, "Because I designed it myself.")
return
Button3:
risky_operation()
return
Button4:
borrowed_function()
return
Button5:
msgBox % "There is nothing wrong with clicking this button."
return
Guiclose:
GuiEscape:
ExitApp
risky_operation()
{
Random, random_number, 0, 1
if random_number
{
Throw, Exception("You knew this was a posibility!")
}
else
{
msgBox % "Nothing to see here."
}
}
borrowed_function()
{
; ...
; code formatted according to someone else's standards
; ...
Throw, Exception("Hah! Gotcha.")
; ...
}
Email_Helpdesk_Exception(p_exception)
{
; magic happens here
}