C++ - 完全暂停Windows应用程序
我正在开发一个简单的 WinAPI 应用程序,并从编写自己的断言系统开始。
我有一个像 ASSERT(X)
那样定义的宏,它与 assert(X)
所做的事情几乎相同,但有更多信息、更多选项等
。那一刻(当断言系统已经运行并工作时)我意识到存在问题。
假设我编写了一段使用计时器执行某些操作的代码,并且(只是一个简单的示例)该操作是在处理 WM_TIMER
消息时完成的。现在,情况改变了该代码开始抛出断言的方式。此断言消息将每TIMER_RESOLUTION
毫秒显示一次,并且会简单地淹没屏幕。
解决这种情况的选项可能是:
1) 当显示断言消息框时完全暂停应用程序运行(也可能暂停所有线程)并在关闭后继续运行
2) 为显示的断言创建一个静态计数器并且不显示当其中之一已显示时断言(但这不会暂停应用程序)
3) 将类似的断言分组,并为每种断言类型仅显示一个断言(但这也不会暂停应用程序)
4) 修改应用程序代码(例如, Get / Translate / Dispatch
消息循环),以便在有任何断言时它会自行挂起。这很好,但不通用,看起来像黑客。
在我看来,选项 1 是最好的。但我不知道如何实现这一目标。我正在寻找一种暂停运行时的方法(类似于调试器中的 Pause
按钮)。有人知道如何实现这一目标吗?
另外,如果有人知道处理这个问题的有效方法 - 我将不胜感激您的帮助。谢谢。
I am developing a simple WinAPI application and started from writing my own assertion system.
I have a macro defined like ASSERT(X)
which would make pretty the same thing as assert(X)
does, but with more information, more options and etc.
At some moment (when that assertion system was already running and working) I realized there is a problem.
Suppose I wrote a code that does some action using a timer and (just a simple example) this action is done while handling WM_TIMER
message. And now, the situation changes the way that this code starts throwing an assert. This assert message would be shown every TIMER_RESOLUTION
milliseconds and would simply flood the screen.
Options for solving this situation could be:
1) Totally pause application running (probably also, suspend all threads) when the assertion messagebox is shown and continue running after it is closed
2) Make a static counter for the shown asserts and don't show asserts when one of them is already showing (but this doesn't pause application)
3) Group similiar asserts and show only one for each assert type (but this also doesn't pause application)
4) Modify the application code (for example, Get / Translate / Dispatch
message loop) so that it suspends itself when there are any asserts. This is good, but not universal and looks like a hack.
To my mind, option number 1 is the best. But I don't know any way how this can be achieved. What I'm seeking for is a way to pause the runtime (something similiar to Pause
button in the debugger). Does somebody know how to achieve this?
Also, if somebody knows an efficient way to handle this problem - I would appreciate your help. Thank you.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
要回答这个问题,了解 Windows UI 程序的工作原理非常重要。
Windows UI 编程模型的核心当然是“消息队列”。消息到达消息队列并使用消息泵检索。消息泵并不特殊。它只是一次检索一条消息的循环,如果没有可用的线程,则阻塞线程。
现在为什么会出现所有这些对话框,包括 MessageBox 也有一个消息泵,因此它们将从消息队列中检索消息(谁并不重要。 在 Windows 模型中,这允许绘画、鼠标移动和键盘输入工作,因此
,规范的 Windows 方法是每当消息出现时就对其进行处理 。它们是生活中的事实,
在您的情况下,我会考虑稍微改变一下断言发生时的堆栈状态。受到尊重。因此,为对话框分出一个线程,并在没有父 HWND 的情况下创建它。这为对话框提供了一个独立的消息队列,独立于原始窗口。由于还有一个新线程,因此您可以挂起原始线程,即 WM_TIMER 到达的线程。
It is important to understand how Windows UI programs work, to answer this question.
At the core of the Windows UI programming model is of course "the message" queue". Messages arrive in message queues and are retrieved using message pumps. A message pump is not special. It's merely a loop that retrieves one message at a time, blocking the thread if none are available.
Now why are you getting all these dialogs? Dialog boxes, including MessageBox also have a message pump. As such, they will retrieve messages from the message queue (It doesn't matter much who is pumping messages, in the Windows model). This allows paints, mouse movement and keyboard input to work. It will also trigger additional timers and therefore dialog boxes.
So, the canonical Windows approach is to handle each message whenever it arrives. They are a fact of life and you deal with them.
In your situation, I would consider a slight variation. You really want to save the state of your stack at the point where the assert happened. That's a particularity of asserts that deserves to be respected. Therefore, spin off a thread for your dialog, and create it without a parent HWND. This gives the dialog an isolated message queue, independent of the original window. Since there's also a new thread for it, you can suspend the original thread, the one where WM_TIMER arrives.
不显示提示 - 要么记录到文件/调试输出,要么只是强制中断调试器(通常是特定于平台的,例如 Microsoft 的 __debugbreak())。如果涉及可能引发大量故障的线程,您必须做一些比显示对话框更被动的事情。
Don't show a prompt - either log to a file/debug output, or just forcibly break the debugger (usually platform specific, eg. Microsoft's __debugbreak()). You have to do something more passive than show a dialog if there are threads involved which could fire lots of failures.
为调试代码创建一个工作线程。当断言发生时,向工作线程发送一条消息。工作线程将在每个线程上调用 SuspendThread停止它,然后显示一个消息框。
要获取进程中的线程 - 创建一个 dll 并监视 DllMain 的线程附加(和分离) - 每个调用都将在创建(或销毁)线程的上下文中完成,以便您可以获得当前线程 id 并创建与 SuspendThread 一起使用的句柄。
或者,toolhelp 调试 api 将帮助您找到出线程暂停。
我更喜欢这种方法的原因是,我不喜欢引起副作用的断言。我经常从异步套接字处理或窗口消息处理代码中触发断言,然后在该线程上创建断言消息框,这会导致线程的状态被完全意外的重入点破坏 - MessageBox 也是如此丢弃发送到线程的所有消息,因此它会扰乱使用线程消息队列对作业进行排队的所有工作线程。
Create a worker thread for your debugging code. When an assert happens, send a message to the worker thread. The worker thread would call SuspendThread on each thread in the process (except itself) to stop it, and then display a message box.
To get the threads in a process - create a dll and monitor the DllMain for Thread Attach (and Detach) - each call will be done in the context of a thread being created (or destroyed) so you can get the current thread id and create a handle to use with SuspendThread.
Or, the toolhelp debug api will help you find out the threads to pause.
The reason I prefer this approach is, I don't like asserts that cause side effects. Too often Ive had asserts fire from asynchronous socket processing - or window message - processing code - then the assert Message box is created on that thread which either causes the state of the thread to be corrupted by a totally unexpected re-entrancy point - MessageBox also discards any messages sent to the thread, so it messes up any worker threads using thread message queues to queue jobs.
我自己的 ASSERT 实现调用 DebugBreak() 或作为替代 INT 3(MS VC++ 中的
__asm int 3
)。 ASSERT 应该在调试器上中断。My own ASSERT implementation calls DebugBreak() or as alternative INT 3 (
__asm int 3
in MS VC++). An ASSERT should break on the debugger.使用消息框函数。这将阻塞,直到用户单击“确定”。完成此操作后,您可以选择放弃额外的断言失败消息或仍然显示它们作为您的选择。
Use the MessageBox function. This will block until the user clicks "ok". After this is done, you could choose to discard extra assertion failure messages or still display them as your choice.