65.9K
CodeProject 正在变化。 阅读更多。
Home

BreakInfinite

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.37/5 (17投票s)

2005 年 11 月 14 日

CPOL

5分钟阅读

viewsIcon

33394

downloadIcon

463

一套用于在 DEBUG 模式下(VC++)中断无限循环的宏。

引言

Demo screen-shot

应用程序越复杂,我们就越关注我们编写的代码要提供的功能。有时我们会沉迷于自己的想法,以至于忘记放一个分号(或者把它放在不该放的地方)。在大多数情况下,当我们尝试编译代码时,我们就能摆脱这个问题。这是语法错误的情况(不要声称自己是“无错误”开发者,我不信)。当处理逻辑错误时,情况会变得更糟。一种常见的做法是使用通过 `for` 或 `while` 块实现的无限循环,并决定何时退出循环。代码块比预期的要长一点,有人可能会忘记编写负责中断循环的代码行。其他时候,人们可能会使用一个有明确退出条件的循环,但不小心改变了由于一系列计算而参与循环条件的索引。还有一些情况,人们可能会使用 `while` 循环从记录集中提取数据,以 `EOF` 到达为退出循环的条件,但却忘记调用 `MoveNext()`。最后一个情况是我前段时间遇到的。当然,我找到了错误,但使用众所周知的 CTRL-ALT_DEL 组合键(上帝保佑 CTRL-ALT_DEL)好几次,这有点令人尴尬。反正,这不是重点。我的重点是寻找一种方法,使编程环境尽可能地了解我的编程行为,并允许我在处理循环时能够快速从逻辑错误中恢复并定位它们。

我想要一种方法,在看到应用程序挂起时能够按下一个键,并在获得有关挂起原因的提示时重新获得应用程序的控制权,如果可能的话,还能获得应用程序挂起时获得焦点的控件的提示。我发现使用宏可以提供很大帮助。这是 BreakInfinite.h 文件,你只需将其包含在 StdAfx.h 中,即可帮助编程环境解决 无限循环 问题。

使用 BreakInfinite

首先,将 BreakInfinite.h 文件复制到 Visual Studio 使用的包含文件夹之一。然后,在你的 StdAfx.h 文件中添加以下行:

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

注意BreakInfinite 支持仅在 DEBUG 模式下激活。

为了激活对 `while`-循环的 BreakInfinite 支持,请使用大写字母写 `while`,即 `WHILE`。后台的 `_fn_while()` 函数将让你从挂起的应用程序中重新获得控制权。

WHILE Macro

为了激活对 `for`-循环的 BreakInfinite 支持,请使用 `FOR / NEXT` 对而不是 `for` 和随附的 `{}` 括号。后台的 `_fn_for()` 函数将让你从挂起的应用程序中重新获得控制权。

FOR Macro

我编写了一个小型基于对话框的应用程序来在 DEBUG 模式下测试 BreakInfinite 功能。两个按钮会将应用程序带入无限循环。通过按 F8 键,会弹出一个消息框,指示无限循环条件(`while` 或 `for`)以及应用程序挂起时获得焦点的控件的标题。同时,在该控件上会出现一个感叹号图标。

Break infinite loop

关闭消息框后,感叹号图标会从控件上消失,应用程序也不再挂起。

BreakInfinite 还为进一步使用其功能提供了支持,无论你在何处需要。你可能希望坚持使用传统的 `while` 样式,并在 RELEASE 模式下使用中断功能。或者,你可能希望在按下 F8 键时采取某些操作。为此,只需在你代码中放置一个对 `_fn_continue()` 的调用即可。

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

幕后代码

通过在 RELEASE 模式下定义以下宏,`WHILE` 变成简单的 `while`,而 `FOR / NEXT` 则变回 `for` 及其随附的 `{}` 括号。

#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

DEBUG 模式下,布尔 `WHILE` 条件被 `inline` 函数 `_fn_while()` 返回的布尔值替换,该函数将原始 `WHILE` 条件作为参数传递。对于 `FOR / NEXT` 块,`FOR` 被经典的 `for` 加上开括号 `{` 替换。`NEXT` 被对 `inline` 函数 `_fn_for()` 返回的布尔值的检查和闭括号 `}` 替换。上面提到的两个函数都通过调用另一个 `inline` 函数 `_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()` 是 BreakInfinite 功能的核心。它需要两个参数:一个指向窗口的指针,它将在该窗口的消息队列中查找与按下 F8 键相关的键盘消息;以及一个指向以 null 结尾的字符串的指针,用于构建响应该按键事件而弹出的消息框的文本消息。

`_fn_continue()` 首先检查线程消息队列中是否有键盘消息,并将消息(如果有)放入 `MSG` 结构(`theMsg`)中。

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

如果存在这样的消息,它会检查消息是否与按下 F8 键有关。如果不存在与 F8 相关的键盘消息,函数将翻译并分派消息(如果有),然后返回 `TRUE`,告诉应用程序继续执行之前的操作。

如果按下了 F8 键,`_fn_continue()` 会获取当前获得焦点的控件的处理程序,并弹出一个消息框,显示无限循环条件以及该控件的标题(如果存在)。通过弹出消息框,你获得了对挂起应用程序的控制,因为函数在消息框关闭后返回 `FALSE`。这个 `FALSE` 返回值进一步用作中断循环的条件。因此,应用程序不再挂起。

历史

  • 创建时间:2005 年 11 月。
BreakInfinite - CodeProject - 代码之家
© . All rights reserved.