我正在开发一个简单的WinAPI应用程序,并从编写自己的断言系统开始。
我有一个定义为ASSERT(X)
的宏,它与assert(X)
的内容完全相同,但有更多信息,更多选项等。
在某个时刻(当断言系统已经运行并正在运行时)我意识到存在问题。
假设我编写了一个使用计时器执行某些操作的代码,并且(只是一个简单的示例)此操作在处理WM_TIMER
消息时完成。现在,情况改变了这段代码开始抛出断言的方式。这个断言消息将每隔TIMER_RESOLUTION
毫秒显示一次,并且只会泛滥屏幕。
解决这种情况的选项可能是:
1)在显示断言消息框时完全暂停应用程序运行(可能还会暂停所有线程)并在关闭后继续运行
2)为显示的断言创建一个静态计数器,并且当其中一个断言已经显示时不显示断言(但这不会暂停应用)
3)对类似的断言进行分组,并且每个断言类型只显示一个(但这也不会暂停应用)
4)修改应用程序代码(例如,Get / Translate / Dispatch
消息循环),以便在有任何断言时自行挂起。这很好,但不是普遍的,看起来像黑客。
在我看来,选项编号1是最好的。但我不知道如何实现这一目标。我正在寻找的是一种暂停运行时的方法(类似于调试器中的Pause
按钮)。有人知道如何实现这个目标吗?
另外,如果有人知道处理这个问题的有效方法 - 我将非常感谢你的帮助。谢谢。
答案 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功能。这将阻止,直到用户单击“确定”。完成此操作后,您可以选择丢弃额外的断言失败消息,或者仍然将其显示为您的选择。