C ++ - 完全暂停Windows应用程序

时间:2010-05-21 11:33:25

标签: c++ winapi assert

我正在开发一个简单的WinAPI应用程序,并从编写自己的断言系统开始。

我有一个定义为ASSERT(X)的宏,它与assert(X)的内容完全相同,但有更多信息,更多选项等。

在某个时刻(当断言系统已经运行并正在运行时)我意识到存在问题。

假设我编写了一个使用计时器执行某些操作的代码,并且(只是一个简单的示例)此操作在处理WM_TIMER消息时完成。现在,情况改变了这段代码开始抛出断言的方式。这个断言消息将每隔TIMER_RESOLUTION毫秒显示一次,并且只会泛滥屏幕。

解决这种情况的选项可能是:

1)在显示断言消息框时完全暂停应用程序运行(可能还会暂停所有线程)并在关闭后继续运行

2)为显示的断言创建一个静态计数器,并且当其中一个断言已经显示时不显示断言(但这不会暂停应用)

3)对类似的断言进行分组,并且每个断言类型只显示一个(但这也不会暂停应用)

4)修改应用程序代码(例如,Get / Translate / Dispatch消息循环),以便在有任何断言时自行挂起。这很好,但不是普遍的,看起来像黑客。

在我看来,选项编号1是最好的。但我不知道如何实现这一目标。我正在寻找的是一种暂停运行时的方法(类似于调试器中的Pause按钮)。有人知道如何实现这个目标吗?

另外,如果有人知道处理这个问题的有效方法 - 我将非常感谢你的帮助。谢谢。

5 个答案:

答案 0 :(得分:4)

了解Windows UI程序的工作原理非常重要,以回答这个问题。

Windows UI编程模型的核心当然是“消息”队列“。消息到达消息队列并使用消息泵检索。消息泵并不特殊。它只是一个循环,它在一段时间,如果没有可用的话就阻塞线程。

现在为什么要获得所有这些对话框?包括MessageBox在内的对话框也有一个消息泵。因此,他们将从消息队列中检索消息(在Windows模型中正在抽取消息并不重要)。这允许绘画,鼠标移动和键盘输入工作。它还会触发其他计时器,从而触发对话框。

因此,规范的Windows方法是在每条消息到达时处理它们。他们是生活中的事实,你可以处理它们。

在您的情况下,我会考虑稍微变化。您确实希望在断言发生时保存堆栈的状态。这是一个值得尊重的断言的特殊性。因此,为对话框分离一个线程,并在没有父HWND的情况下创建它。这使对话框成为一个独立于原始窗口的独立消息队列。由于还有一个新的线程,你可以暂停原始线程,即WM_TIMER到达的线程。

答案 1 :(得分:2)

不显示提示 - 要么记录到文件/调试输出,要么只是强行破坏调试器(通常是特定于平台的,例如Microsoft的__debugbreak())。如果涉及线程涉及可能引发大量失败的话,你必须做一些比展示对话更被动的事情。

答案 2 :(得分:2)

为您的调试代码创建一个工作线程。发生断言时,向工作线程发送消息。工作线程会在进程中的每个线程上调用SuspendThread(除了它自己)以阻止它,然后显示一个消息框。

要获取进程中的线程 - 创建一个dll并监视DllMain for Thread Attach(和Detach) - 每个调用都将在创建(或销毁)线程的上下文中完成,这样您就可以获得当前线程id并创建一个与SuspendThread一起使用的句柄。

或者,toolhelp debug api将帮助您找出要暂停的线程。

我更喜欢这种方法的原因是,我不喜欢引起副作用的断言。我常常断言来自异步套接字处理 - 或窗口消息 - 处理代码 - 然后在该线程上创建断言消息框,这会导致线程的状态被完全意外的重入点破坏 - MessageBox也丢弃发送给线程的任何消息,因此它会使用线程消息队列来排队任何工作线程。

答案 3 :(得分:1)

我自己的ASSERT实现调用DebugBreak()或替代INT 3(MS VC ++中的__asm int 3)。 ASSERT应该在调试器上中断。

答案 4 :(得分:0)

使用MessageBox功能。这将阻止,直到用户单击“确定”。完成此操作后,您可以选择丢弃额外的断言失败消息,或者仍然将其显示为您的选择。