MessageBox Аномальное завершение программы поддерживает работу моего приложения

...вроде. Как показано на этом крайне упрощенном примере,

введите здесь описание изображения

очень редко (пока сообщалось только один раз) случается, что одно из моих приложений вылетает таким образом. Я хочу завершить его, как обычно, когда возникает неспецифическое исключение. Моя стратегия состоит в том, чтобы (низкоуровнево) зарегистрировать проблему, а затем завершить ее. Приложение является частью подсистемы, и я хочу (пере)запустить его, если будет обнаружена какая-либо проблема. Он построен с помощью C++-Builder 6 и работает в Windows (XP...7, а также 8). Я узнал, что abort(), скорее всего, вызвало сообщение об ошибке. Приложение имеет графический интерфейс, поэтому отображается окно сообщения, а не просто вывод (разблокировка) на stderr.

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

После прочтения некоторых ответов на Какой самый простой способ вызвать сбой программы на C++? и Разница между методами повышения (SIGABRT) и abort(), я попробовал следующее

void mySignalHandler(int sig)
{
    // low-level error reporting here
    exit(-1);
}

void __fastcall TForm1::FormCreate(TObject *Sender)
{
    signal(SIGABRT, mySignalHandler);
    // some more initialisation here
}

что позволяет моему приложению завершить работу правильно, даже если вызывается abort() или raise(SIGABRT). (Я также хочу, чтобы Windows не «искала решение проблемы».)

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


person Wolf    schedule 14.08.2014    source источник
comment
Почему бы просто не попытаться отладить программу, чтобы найти причину сбоев? Если вы исправите сбои, они больше не повторятся.   -  person Some programmer dude    schedule 14.08.2014
comment
Использование API сигналов POSIX для выявления проблем с Windows не будет работать так хорошо. Используйте СЭХ.   -  person bmargulies    schedule 14.08.2014
comment
@JoachimPileborg это не просто, но я делаю это. При этом система должна работать в любом случае...   -  person Wolf    schedule 14.08.2014
comment
@bmargulies Пользователь сообщил об этом окне сообщения (только один раз до сегодняшнего дня), мое приложение не использует сигналы POSIX, это должно быть вызвано какой-то библиотекой.   -  person Wolf    schedule 14.08.2014
comment
Попробуйте обычный выход с кодом, а не прерывание или подъем, потому что эти функции вызывают дополнительные возможности ОС.   -  person Tanuki    schedule 14.08.2014
comment
@Tanuki Я не звоню abort или raise, мне нужно с этим разобраться.   -  person Wolf    schedule 14.08.2014
comment
bmargulies: SIGABRT есть не только в POSIX, но и в стандарте C.   -  person Shao    schedule 14.08.2014
comment
@JoachimPileborg, возможно, вы не решите все проблемы ... рекомендуется, чтобы ваша программа завершилась без ошибок на случай, если она выйдет из строя по непредвиденным причинам.   -  person M.M    schedule 14.08.2014
comment
И пока окно сообщения не принимается пользователем, мое приложение, очевидно, продолжает работать, - вот   -  person M.M    schedule 14.08.2014
comment
@MattMcNabb ясно: за исключением того, что пользовательский ввод не принимается, потому что графический интерфейс заблокирован (как обычно для модальных диалогов). Кажется, у какого-то разработчика Borland была отличная идея.   -  person Wolf    schedule 14.08.2014
comment
C++Builder поставляется с исходным кодом для RTL и VCL, поэтому, если он действительно исходит оттуда, вы сможете изменить поведение. Я бы сказал, что это происходит только из-за неперехваченных исключений, отличных от VCL.   -  person M.M    schedule 14.08.2014
comment
Это не диалог Borland. Это системный диалог.   -  person David Heffernan    schedule 14.08.2014
comment
@DavidHeffernan, ты уверен? Какая система: Windows? (я добавил скриншот)   -  person Wolf    schedule 14.08.2014
comment
Нет, наверное, ты прав. Итак, теперь вы можете искать этот текст в исходном коде RTL/VCL и выяснять, что его вызывает.   -  person David Heffernan    schedule 14.08.2014


Ответы (4)


В папке установки C++Builder проверьте наличие следующих файлов:

  • source\cpprtl\Source\misc\errormsg.c - реализация _ErrorMessage
  • source\cpprtl\Source\procses\abort.c - реализация abort, которая вызывает _ErrorMessage
  • source\cpprtl\Source\misc\assert.c - реализация _assert, которая вызывает _ErrorMessage

errormsg.c определяет недокументированный указатель функции _messagefunc, который можно настроить для переопределения поведения по умолчанию. Хотя он недокументирован и не объявлен ни в одном заголовочном файле, вы можете объявить его как extern и таким образом получить к нему доступ. Пример использования:

extern int (_RTLENTRY * _EXPDATA _messagefunc)(char *msg);

static int LogAndDie(char *msg)
{
  LogMessageToSomeFile(msg);
  exit(1);
  return 0;
}

void InitializeErrorHandling()
{
  _messagefunc = LogAndDie;
}
person Josh Kelley    schedule 14.08.2014
comment
Я думаю, что ваше предложение нуждается в переосмыслении. Функция, на которую указывает _messagefunc, должна обрабатывать только сообщение об ошибке, т. е. регистрировать его. Она косвенно вызывается _ErrorMessage, и эта функция также используется в нефатальных обстоятельствах. - person Wolf; 19.08.2014
comment
@Волк - Хм. Мы запускаем код с использованием _messagefunc в производственной среде в течение нескольких лет (не завершая работу, как вы хотите, а используя _messagefunc для преобразования ошибок в отчеты о проблемах для нашего средства отслеживания проблем). Единственные нефатальные ошибки, которые я видел на практике, — это математические ошибки (например, получение log(0)), которые могут не быть проблемой для вашего домена приложения. В любом случае проверки strcmp(msg, "Abnormal program termination") == 0 должно быть достаточно, чтобы получить только фатальные ошибки. - person Josh Kelley; 19.08.2014
comment
Предложенное решение не помогло, но предложение по исследованию помогло. Это помогло мне лучше понять, что происходит. Поэтому я принимаю это как лучший и самый полезный ответ на данный момент. - person Wolf; 12.03.2015

Возможно, вы сможете использовать отчеты об ошибках Windows для создания дампа процесса, когда необработанное исключение вызывает завершение. Затем вы можете просмотреть дамп на досуге и позволить какому-либо родительскому процессу или другому сторожевому таймеру перезапустить ваш процесс. Если бы вы выбрали эту стратегию, вы бы не пытались справиться с ошибкой в ​​своем коде, а допустили бы ее.

person Shao    schedule 14.08.2014
comment
Помимо идеи отчетов об ошибках Windows (мне еще предстоит изучить): я не думаю, что в этом случае есть летающее исключение. - person Wolf; 14.08.2014

Если вы хотите зафиксировать любой выход из программы, вам следует обратиться к atexit(). Если вы хотите зафиксировать все события завершения, посмотрите std::set_terminate(), если вы хотите перехватить все непредвиденные исключения, посмотрите std::set_unexpected(). Если вы хотите захватить только abort(), вы можете вызвать signal() с SIGABRT значение сигнала. Вы также можете обернуть свой код try{your code}catch(...){custom event handler}.

person Community    schedule 14.08.2014
comment
Возможно, я недостаточно ясно выразился по этому поводу: на самом деле исключения — это не моя проблема, а скорее отсутствие исключений. - person Wolf; 14.08.2014
comment
If you want to capture only abort() you can call signal() with the SIGABRT signal value. Именно так я и поступил. Нет сомнений, что это технически возможно. (Вся остальная информация в ваших ответах не имеет отношения к моей проблеме, поэтому, извините, -1) Вместо этого я хочу знать, как правильно выйти из моей программы при вызове этой функции. Я постараюсь добавить эту деталь к вопросу. - person Wolf; 15.08.2014
comment
@Wolf Я ответил так, как понял ваш вопрос, когда вы впервые задали его здесь - с тех пор у вас было 6 правок ваших исходных вопросов. Вы сами признали выше, что, возможно, были недостаточно ясны. - person ; 15.08.2014
comment
Я чувствовал, что ваш ответ не соответствует первой версии вопроса stackoverflow.com/posts/25305653/timeline, и я сказал, что в моем первом комментарии. Ваш ответ о возможных ошибках программирования действительно далеко не распространен (сравните его, например, с этим ответом). Не могли бы вы быть более конкретным? - person Wolf; 15.08.2014
comment
@Wolf Если ваш вопрос был ясен с самого начала, то зачем вам потом понадобилось шесть правок на ваш вопрос? Возможно, вам не следует голосовать против людей, когда проблема заключается в вашем собственном плохо написанном вопросе. - person ; 17.08.2014
comment
Ссылка, которую вы дали, указывает, что функция, на которую указывает func, автоматически вызывается без аргументов, когда программа завершается нормальным образом. В моем случае это ненормальное завершение... - person Wolf; 19.08.2014
comment
@Wolf Использование std::set_terminate() без вызова abort() из функции обработчика завершения предотвратит ненормальное завершение. - person ; 19.08.2014
comment
Если я вызываю abort(), этот обработчик завершения не вызывается, вместо этого я вижу нарушение прав доступа (то же самое, что и без std::set_terminate) - я знаю механизм set-terminate только в сочетании с неперехваченными исключениями... - person Wolf; 19.08.2014
comment
@Wolf abort() может запускать или не запускать обработчики atexit(), вы можете попробовать. - person ; 19.08.2014
comment
пробовал в консольном приложении. throw 0; вызывает вызов моего обработчика (мое завершение на stderr), тогда как abort(); выдает ненормальное завершение программы на выходе. - person Wolf; 19.08.2014

Я мог бы провести некоторые тесты, и я могу только подтвердить, что регистрация обработчика сигнала SIGABRT — это просто NOOP.

Я попробовал это с очень простым приложением с графическим интерфейсом, написанным с помощью VS2008 Express. :

  • ни фреймворка, ни .NET, а только Win API
  • одно меню с Exit и Fatal
  • меню, управляемое непосредственно в WndProc
  • Фатальная казнь 1/0

Вот результат:

  • никаких специальных действий => Windows открывает MessageBox, указывающий на фатальную ошибку...
  • обработчик сигнала для SIGABRT => тот же MessageBox
  • С++ try catch(...) => тот же MessageBox
  • SEH в WndProc: может перехватить ошибку!
  • SEH вокруг цикла сообщений: может перехватить ошибку!

Если я ставлю обработчики SEH бота, то ловит самый внутренний (WndProc).

Хорошей новостью для вас является то, что этого достаточно для защиты цикла обработки сообщений, и вам не нужно заходить в каждый WndProc.

Плохая новость в том, что я не знаю компоновщика C++ и не могу сказать, где найти цикл обработки сообщений.

Просто чтобы дать вам подсказку, вот как я могу защитить цикл обработки сообщений в приложении WinAPI:

__try {
while (GetMessage(&msg, NULL, 0, 0))
{
    if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}
}
__except (EXCEPTION_EXECUTE_HANDLER){
    ::MessageBox(NULL, _T("FATAL"), _T("MAIN"), MB_OK | MB_ICONERROR);
}

Таким образом, я могу видеть свое собственное окно сообщения, но ничего больше, и если я прокомментирую свое окно сообщения, приложение автоматически закрывается.

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

Надеюсь, поможет ...

person Serge Ballesta    schedule 14.08.2014
comment
Спасибо за ваше исследование! Приложения с графическим интерфейсом C++-Builder(6) основаны на среде VCL. Цикл сообщений запускается Application::Run и не зависит напрямую от источника приложения. Я хотел бы лучше придерживаться этой конвенции. - person Wolf; 14.08.2014
comment
Я когда-то (очень, очень давно) использовал компилятор BC, если я правильно помню, основной класс приложения расширял приложение, и можно было переопределить многие вещи (включая запуск или ... цикл). Можно ли еще так сделать? - person Serge Ballesta; 14.08.2014
comment
Это было в старые добрые времена TVision, это изменилось под Windows, потому что сама Windows основана на собственная концепция GUI, управляемая событиями. - person Wolf; 14.08.2014
comment
Не такой уж старый, время Windows 3.11, но он все еще далеко в моей памяти ... Поскольку VCL не является общедоступным, я не могу вам помочь, кроме как посоветовать вам следить за всеми переопределяемыми методами ... - person Serge Ballesta; 14.08.2014