С++ - полностью приостановить приложение Windows

Я разрабатываю простое приложение WinAPI и начал с написания собственной системы утверждений.

У меня есть макрос, определенный как ASSERT(X), который сделает то же самое, что и assert(X), но с большей информацией, дополнительными параметрами и т. д.

В какой-то момент (когда эта система утверждений уже была запущена и работала) я понял, что есть проблема.

Предположим, я написал код, который выполняет какое-то действие с использованием таймера, и (простой пример) это действие выполняется при обработке сообщения WM_TIMER. И теперь ситуация меняется, когда этот код начинает выдавать утверждение. Это сообщение об утверждении будет отображаться каждые TIMER_RESOLUTION миллисекунды и просто заполнит экран.

Вариантами решения этой ситуации могут быть:

1) Полностью приостановить работу приложения (возможно, также приостановить все потоки), когда отображается окно сообщения об утверждении, и продолжить работу после его закрытия.

2) Создайте статический счетчик для показанных утверждений и не показывайте утверждения, когда одно из них уже отображается (но это не приостанавливает приложение)

3) Сгруппируйте похожие утверждения и покажите только одно для каждого типа утверждений (но это также не приостанавливает приложение)

4) Измените код приложения (например, цикл сообщений Get / Translate / Dispatch), чтобы оно приостанавливало себя при наличии каких-либо утверждений. Это хорошо, но не универсально и похоже на хак.

На мой взгляд, вариант номер 1 самый лучший. Но я не знаю, как этого можно добиться. Я ищу способ приостановить выполнение (что-то похожее на кнопку Pause в отладчике). Кто-нибудь знает, как этого добиться?

Кроме того, если кто-то знает эффективный способ справиться с этой проблемой - буду признателен за вашу помощь. Спасибо.


person Yippie-Ki-Yay    schedule 21.05.2010    source источник
comment
Если вы приостановите процесс/поток, как пользователь закроет диалоговое окно сообщения?   -  person Alex K.    schedule 21.05.2010
comment
Я не знаю :) Но я думаю, что должен быть способ приостановить все, кроме этого диалогового окна сообщения   -  person Yippie-Ki-Yay    schedule 21.05.2010
comment
Как assert(x) решает проблему?   -  person avakar    schedule 21.05.2010
comment
На самом деле, он терпит неудачу, а также выводит тонны диалоговых окон утверждений. Но я надеюсь, что есть способ получить поведение, которое я ожидаю.   -  person Yippie-Ki-Yay    schedule 21.05.2010


Ответы (5)


Чтобы ответить на этот вопрос, важно понять, как работают программы пользовательского интерфейса Windows.

В основе модели программирования пользовательского интерфейса Windows, конечно же, лежит «очередь сообщений». Сообщения поступают в очереди сообщений и извлекаются с помощью насосов сообщений. Насос сообщений не является чем-то особенным. Это просто цикл, который извлекает одно сообщение за раз, блокировка потока, если ни один из них не доступен.

Теперь, почему вы получаете все эти диалоги? Диалоговые окна, в том числе MessageBox, также имеют насос сообщений. Таким образом, они будут извлекать сообщения из очереди сообщений (в модели Windows не имеет большого значения, кто отправляет сообщения). Это позволяет работать краскам, движениям мыши и вводу с клавиатуры. Это также вызовет дополнительные таймеры и, следовательно, диалоговые окна.

Таким образом, канонический подход Windows состоит в том, чтобы обрабатывать каждое сообщение всякий раз, когда оно приходит. Они являются фактом жизни, и вы имеете дело с ними.

В вашей ситуации я бы рассмотрел небольшое изменение. Вы действительно хотите сохранить состояние вашего стека в точке, где произошло утверждение. Это особенность утверждений, которая заслуживает уважения. Поэтому выделите поток для своего диалога и создайте его без родительского HWND. Это дает диалогу изолированную очередь сообщений, независимую от исходного окна. Поскольку для него также есть новый поток, вы можете приостановить исходный поток, в который приходит WM_TIMER.

person MSalters    schedule 21.05.2010
comment
Я прав, что приостановка основного потока не универсальна. Я имею в виду, что у меня может быть несколько основных потоков или что-то в этом роде, или даже использовать какую-нибудь стороннюю библиотеку потоков, например boost::thread... Означает ли это, что мне придется писать код утверждения для конкретного приложения? - person Yippie-Ki-Yay; 22.05.2010
comment
На самом деле нет такого понятия, как основной поток. Но это не имеет значения. Вы должны заблокировать поток, который утверждал, независимо от того, какой поток это был. - person MSalters; 25.05.2010

Не показывать подсказку — либо войти в файл/вывод отладки, либо просто принудительно сломать отладчик (обычно зависящий от платформы, например, Microsoft __debugbreak()). Вы должны сделать что-то более пассивное, чем показывать диалоговое окно, если есть задействованные потоки, которые могут привести к большому количеству сбоев.

person AshleysBrain    schedule 21.05.2010

Создайте рабочий поток для кода отладки. Когда происходит утверждение, отправьте сообщение рабочему потоку. Рабочий поток будет вызывать SuspendThread в каждом потоке. в процессе (кроме самого себя), чтобы остановить его, а затем отобразить окно сообщения.

Чтобы получить потоки в процессе — создайте dll и отслеживайте DllMain для присоединения (и отсоединения) потока — каждый вызов будет выполняться в контексте создаваемого (или уничтожаемого) потока, поэтому вы можете получить идентификатор текущего потока и создать дескриптор для использования с SuspendThread.

Или отладочный API toolhelp поможет вам найти из потоков, чтобы приостановить.

Причина, по которой я предпочитаю этот подход, заключается в том, что мне не нравятся утверждения, вызывающие побочные эффекты. Слишком часто у меня возникал пожар утверждений из-за асинхронной обработки сокета или кода обработки оконных сообщений, тогда в этом потоке создается окно сообщения утверждения, которое либо приводит к повреждению состояния потока из-за совершенно неожиданной точки повторного входа - MessageBox также отбрасывает любые сообщения, отправленные в поток, поэтому он портит любые рабочие потоки, используя очереди сообщений потока для постановки в очередь заданий.

person Chris Becke    schedule 22.05.2010
comment
Есть ли у меня возможность также приостанавливать not-winapi потоки, например потоки, созданные с использованием boost::thread или что-то в этом роде? - person Yippie-Ki-Yay; 23.05.2010
comment
все потоки (в Windows) являются потоками winapi: boost::thread, pthread-win32 и т. д. являются оболочками потоков winapi на платформе Windows. - person Chris Becke; 23.05.2010

Моя собственная реализация ASSERT вызывает DebugBreak() или, как альтернативу, INT 3 (__asm int 3 в MS VC++). ASSERT должен сломаться в отладчике.

person Hernán    schedule 21.05.2010

Используйте функцию MessageBox. Это будет заблокировано, пока пользователь не нажмет «ОК». После того, как это будет сделано, вы можете отказаться от дополнительных сообщений об ошибках утверждения или по-прежнему отображать их по своему выбору.

person Puppy    schedule 21.05.2010
comment
Это не будет блокировать обработку цикла сообщений приложением, иначе родительское окно никогда не будет перерисовываться. - person Alex K.; 21.05.2010
comment
К сожалению, это не помогает. Вы можете попробовать генерировать окна через MessageBox самостоятельно в обработчике WM_TIMER, и они просто продолжат генерировать, пока активен таймер. - person Yippie-Ki-Yay; 21.05.2010