【问题标题】:C++ - Totally suspend windows applicationC++ - 完全挂起 Windows 应用程序
【发布时间】:2010-05-21 11:33:25
【问题描述】:

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

我定义了一个类似ASSERT(X) 的宏,它的作用与assert(X) 完全相同,但包含更多信息、更多选项等。

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

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

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

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

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

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

4) 修改应用程序代码(例如,Get / Translate / Dispatch 消息循环),使其在有任何断言时自行挂起。这很好,但不是通用的,看起来像一个 hack。

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

另外,如果有人知道解决此问题的有效方法 - 我将非常感谢您的帮助。谢谢。

【问题讨论】:

  • 如果您暂停进程/线程,用户将如何关闭消息对话框?
  • 我不知道 :) 但我认为应该有一种方法可以暂停除该消息对话框之外的所有内容
  • assert(x) 如何处理问题?
  • 实际上,它失败了并且还绘制了大量的断言对话框。但我希望有办法获得我所期望的行为。

标签: c++ winapi assert


【解决方案1】:

了解 Windows UI 程序的工作原理对于回答这个问题很重要。

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

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

因此,规范的 Windows 方法是在每条消息到达时对其进行处理。它们是生活中的事实,你要处理它们。

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

【讨论】:

  • 我说得对吗,挂起主线程不是通用的。我的意思是,我可以有几个主线程或类似的东西,或者甚至使用一些 3rdparty 线程库,比如boost::thread...这是否意味着我必须编写特定于应用程序的断言代码?
  • 实际上没有“主线程”这样的东西。但这没关系。您应该阻止断言的线程,而与是哪个线程无关。
【解决方案2】:

不要显示提示 - 要么记录到文件/调试输出,要么只是强行中断调试器(通常是特定于平台的,例如 Microsoft 的 __debugbreak())。如果涉及的线程可能会引发很多故障,那么您必须做一些比显示对话框更被动的事情。

【讨论】:

    【解决方案3】:

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

    要获取进程中的线程 - 创建一个 dll 并监视 DllMain 的线程附加(和分离) - 每个调用都将在创建(或销毁)线程的上下文中完成,因此您可以获得当前线程id 并创建一个句柄以与 SuspendThread 一起使用。

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

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

    【讨论】:

    • 我是否有可能也暂停not-winapi 线程,例如使用boost::thread 创建的线程或类似的东西?
    • 所有线程(在 Windows 上)都是 winapi 线程:boost::thread、pthread-win32 等是 Windows 平台上 winapi 线程的包装器。
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

      • 它不会阻止应用程序处理其消息循环,如果这样做,父窗口将永远不会重绘。
      • 不幸的是,这无济于事。您可以尝试在WM_TIMER 处理程序中通过MessageBox 自己生成窗口,它们只是在计时器处于活动状态时继续生成。
      猜你喜欢
      • 2011-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多