Exception Handling Directive

Propose new features and changes
ma53
Posts: 6
Joined: 13 Mar 2015, 18:14
GitHub: ma53

Exception Handling Directive

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.

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.")

    ; ...
}
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?

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."
}

; ...
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.

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

; ...
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.

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
}
Any thoughts? Is such an implementation possible?
User avatar
jmeneses
Posts: 451
Joined: 28 Oct 2014, 11:09
Location: Catalan Republic

Re: Exception Handling Directive

27 May 2015, 10:15

Hi ma53
My English is very bad and not if you look for
See this link:http://www.autohotkey.com/board/topic/6 ... ntry415352
Donec Perficiam
guest3456
Posts: 2434
Joined: 09 Oct 2013, 10:31

Re: Exception Handling Directive

27 May 2015, 10:24

Hi ma53
My English is very bad and not if you look for
See this link:http://www.autohotkey.com/board/topic/6 ... ntry415352
whoa sweet, thanks for that link

ma53
Posts: 6
Joined: 13 Mar 2015, 18:14
GitHub: ma53

Re: Exception Handling Directive

27 May 2015, 11:38

Hi ma53
My English is very bad and not if you look for
See this link:http://www.autohotkey.com/board/topic/6 ... ntry415352
Thanks for the link! I had considered something similar, i.e. running a timed subroutine with a WinWait to continually look for an AutoHotkey error window and grab info out of it before closing it, much as Lexikos has done there. His method is, of course, much more subtle. Ultimately I'd like to have a method that doesn't rely on any kind of bulky work-around. Something that can gracefully pass off the exception object to be manipulated, rather than having to reconstruct the information out of a window, etc.
guest3456
Posts: 2434
Joined: 09 Oct 2013, 10:31

Re: Exception Handling Directive

27 May 2015, 11:56

What I envision is a directive similar to OnMessage...Somewhere I can specify a function to run any time an exception goes uncaught.
What was provided seems like it meets exactly what you asked for above. A timed subroutine is a bit different, in that it continually takes up resources. Lexikos' method is to simply capture the message, which is like a callback that only occurs once the window message is sent.

There may be better or more holisitic approaches to handling errors. I don't know. The way that try/catch is implemented in AHK seems very similar to other languages, so I don't really see how else that should be done.
This doesn't become really necessary until you have a large and complex script with multiple possible threads
Ultimately I think the problem is yours, where you may need to re-think your development model. I too have a very complex script that I distribute to hundreds of end users, with multiple timers/threads/hotkeys running. Debugging problems is a huge pain in the ass at times. You probably need to spend a lot of time digging through your code, seeing how you can prevent conflicts across threads. That starts with eliminating global variables as much as possible, using functions instead of subroutine labels and using dependency injection, etc. Also looking into the use of critical and thread priorities.

Finally, you should look into a TDD (test-driven developement) model, where you write tests for every single case first, and only after that then you write the code to pass those tests. There are two frameworks for AHK: majkinetor's UTest originally built for AHK Basic, and Uberi's Yunit built for AHK_L. Its very tedious at the start, but as your project grows you can have lots of confidence in refactoring your code when you know you have regression tests to fall back on instead of wondering "Am i gonna break something by changing this?" While its always better to start with testing right from the beginning, its never too late

ma53
Posts: 6
Joined: 13 Mar 2015, 18:14
GitHub: ma53

Re: Exception Handling Directive

27 May 2015, 12:55

Ultimately I think the problem is yours, where you may need to re-think your development model.
Thank you for your suggestions. My development model could certainly stand to be improved, however I feel I may have managed to misrepresent myself. I do not personally find myself possessed of a program throwing all sorts of exceptions that I don't understand or didn't expect. Were that the case, I would have posted a topic on the "Ask for Help" board, where I know there are a lot of helpful folks ready, willing, and able to assist. I've simply encountered what I feel to be a plausible use case for functionality that seems to not be implemented in AutoHotkey at this time, and posted that use case in a simple form on the "Wish List" board.

I don't argue that one should generally be aware of what exceptions one's program is capable of throwing, and I acknowledged this in my original post. However, AutoHotkey has already implemented functionality to handle exceptions that make it all the way to the top of the stack, so it's clearly a situation that someone thought should be handled. The way AutoHotkey does this is, at this time, simply rather opaque and immutable, and I think it would be useful to have the ability to extend or replace this functionality so that it might be tailored to an individual's particular needs. The need is further indicated by the existence of threads such as the one jmeneses linked to, in which it is confirmed by Lexikos that such functionality is not currently implemented. He posts a clever workaround which, while immensely useful, is not what I have asked for. I'm not asking for anything. I'm suggesting that perhaps this situation could be addressed with more than a work-around solution.
Kudos
Posts: 6
Joined: 23 Aug 2015, 05:52

Re: Exception Handling Directive

17 Aug 2016, 05:54

Hi,

The workaround does not seem to work on AHK 1.1 (AHK_L)
https://autohotkey.com/board/topic/6567 ... r-messages

It just says 'The maximum number of MsgBoxes has been reached.' Followed by 'Error at line 4. Please contact support' around 7 times.

Any ideas how to fix?

Thanks!
Kudos
lexikos
Posts: 6205
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Exception Handling Directive

20 Apr 2018, 18:33

I have uploaded a test build including the OnError() function which will be added in v1.1.29.00.

Return to “Wish List”

Who is online

Users browsing this forum: No registered users and 5 guests