Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

BreakInfinite

0.00/5 (No votes)
14 Nov 2005 1  
A set of macros used to break the infinite loops at run-time in DEBUG mode (VC++).

Introduction

Demo screen-shot

The more complex the applications get, the more we focus on the functionality to be provided by the code we write. It happens sometimes that we are so absorbed by our ideas that we simply forget to put a semi-colon (or we put it just where it should not be). In most of the cases, we get rid of the problem as soon as we try to compile the code. That is the case of syntax errors (and just do not claim being an "error-free" developer, I will not buy it). Things are going worse when dealing with logical errors. One common practice is to use infinite loops implemented through for or while blocks and decide when the loop is to be left. The code block gets a little bit longer than expected and someone may forget to write the line of code responsible for breaking the loop. At other times, one might use a loop with a well defined condition for leaving the loop, and, by mistake, alter an index involved in the looping condition as a result of a sequence of computations. There are also situations when one may use a while loop for extracting data from a record set, with the reaching of EOF as condition to leave the loop, and simply forget to call MoveNext(). This last one happened to me a little time ago. Of course I found the error, but it was somehow embarrassing using the well-known CTRL-ALT_DEL key sequence (God save CTRL-ALT-DEL) several times. And anyway, that is not the point. My point is looking for a way to make the programming environment be aware as much as possible of my programming act and allow me recover fast from and localize my logical errors when working with loops.

I wanted a means of being able to press a key when seeing the application is hanging, and regain the control over the application while receiving some hint on the cause of hanging and, if possible, a hint on the control which had the focus at the moment the application started hanging. I found that using macros could help a lot. And here is the BreakInfinite.h file, which you simply include in StdAfx.h to help the programming environment help you in the infinite looping issue.

Working with BreakInfinite

Start by copying the BreakInfinite.h file in one of the folders used by Visual Studio as include folders. Then add the following line in your StdAfx.h file:

  .
  .
#endif // _AFX_NO_AFXCMN_SUPPORT

#include "BreakInfinite.h" // <-- this is your new line
//{{AFX_INSERT_LOCATION}}
  .
  .

Note: The BreakInfinite support is active only in DEBUG mode.

In order to activate the BreakInfinite support for while-loops, write while with capital letters, i.e. WHILE. The _fn_while() function behind the scene will let you gain the control back from the hanging application.

WHILE Macro

In order to activate the BreakInfinite support for for-loops, use the FOR / NEXT pair instead of for and the accompanying {} brackets. The _fn_for() function behind the scene will let you gain the control back from the hanging application.

FOR Macro

I wrote a small dialog-based application to test the BreakInfinite functionality in DEBUG mode. Two buttons throw the application in infinite loops. By pressing the F8 key, a message box pops up indicating the infinite loop condition (while or for) and the caption of the control which had the focus at the moment the application started hanging. At the same time, an exclamation icon appears over that control.

Break infinite loop

After closing the message box, the exclamation icon disappears off the control and the application hangs no longer.

The BreakInfinite also provides support for further use of its functionality, wherever you may need it. You may want to stick to the classical while style and use the break feature in RELEASE mode. Or you may want to take some action on pressing of the F8 key. For that, you just have to place a call to the _fn_continue() within your code:

if ( !_fn_continue(this, "Some message") ) break;
// or other action instead of <CODE>break

The code behind

Through the definition of the following macros, in RELEASE mode, WHILE becomes simply while, and FOR / NEXT turns back to for and its accompanying {} brackets.

#ifdef _DEBUG
    #if !defined(_INFINITE_LOOP_BREAK_)
        #define _INFINITE_LOOP_BREAK_
        // Breaking the while-loop

        #define WHILE(CONDITION) while ( _fn_while(CONDITION, this) )
        // Breaking the for-loop

        #define FOR(BLOCK_EXPR) for ( BLOCK_EXPR ) {
        #define NEXT if ( !_fn_for(this) ) break; }
    #endif // _INFINITE_LOOP_BREAK_

#else
    #if !defined(_INFINITE_LOOP_BREAK_)
        #define _INFINITE_LOOP_BREAK_
        #define WHILE while
        #define FOR(BLOCK_EXPR) for ( BLOCK_EXPR ) {
        #define NEXT }
    #endif // _INFINITE_LOOP_BREAK_

#endif // _DEBUG

In DEBUG mode, the boolean WHILE-condition is replaced with the boolean value returned by the inline function _fn_while() to which the original WHILE-condition is passed as parameter. In the case of the FOR / NEXT block, FOR is replaced by the classical for followed by the opening { bracket. NEXT is replaced with the check of the boolean value returned by the inline function _fn_for() and the closing } bracket. Both of the previously mentioned functions decide on the value they return upon the result of calling another inline function: _fn_continue():

inline BOOL _fn_while(BOOL bCondition, CWnd *pWnd)
{
    if ( !_fn_continue(pWnd, "Infinite while-loop") ) return FALSE;
    else return bCondition;
}

inline BOOL _fn_for(CWnd *pWnd)
{
    return _fn_continue(pWnd, "Infinite for-loop");
}

_fn_continue() is the core of the BreakInfinite functionality. It needs two parameters: a pointer to the window in whose message queue it looks for the keyboard message related to pressing the F8 key, and a pointer to a null-terminated string used to build the text message shown by the message box that pops up in response to that key event.

_fn_continue() starts by checking the thread message queue for a keyboard message and places the message (if any) in a MSG structure (theMsg):

PeekMessage(&theMsg, pMsgWnd->m_hWnd, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)

Should such a message be available, it checks if the message is related to pressing the F8 key. If no F8-related keyboard message is present, the function translates and dispatches the message (if any) and returns TRUE, telling the application to continue whatever it was doing before.

In case the F8 key was pressed, _fn_continue() gets the handler of the control currently having the focus and pops up a message box showing the infinite loop condition and the caption (if any) of that control. Through popping-up the message box, you get the control from the hanging application, as the function returns FALSE after the message box closes. The FALSE return value is further used as a condition to break the loop. Therefore the application hangs no longer.

History

  • Created: November, 2005.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here