MessageBox “Abnormal program termination” keeps my application running
……种类。正如这个极其简单的例子所说明的,
很少(到目前为止只有一次报告),我的一个应用程序会以这种方式崩溃。我想像往常一样在发生非特定异常时终止它。我的策略是(低级)记录问题,然后终止。应用程序是子系统的一部分,如果检测到任何问题,我希望(重新)启动它。它是用C++Builder 6构建的,在Windows上运行(XP…7,也有8)。我了解到一个abort()很可能导致了错误消息。应用程序有一个图形用户界面,这就是为什么显示一个消息框而不只是向stderr输出(解除锁定)。
只要消息框不被用户接受,我的应用程序就会保持明显的运行,例如它处理计时器(上面例子中的生命周期增加)或进程间消息,完全不知道问题所在。
在阅读了一些什么是最简单的方法使C++程序崩溃的答案?以及raise(sigabrt)和abort()方法之间的区别,我尝试了以下方法
1 2 3 4 5 6 7 8 9 10 11
| 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"搜索问题的解决方案"。)
从您的角度来看,这(注册一个用于中止和调用退出的信号处理程序)是否可靠?…或者至少是可以建立的东西?
- 为什么不试着调试程序来找出崩溃的原因呢?如果你解决了车祸,就不会再发生了。
- 使用POSIX信号API来捕获Windows问题并没有那么好的效果。使用SEH。
- @Joachimileborg这并不简单,但我会这么做。同时,系统必须运行…
- @bmargulies用户报告了这个消息框(直到今天只有一次),我的应用程序不使用POSIX信号,这一定是由某个库引起的。
- 尝试使用代码normarl exit,而不是中止或提升,因为这些函数调用其他操作系统设施。
- @Tanuki我不叫abort或raise,我必须处理它。
- bmargulies:sigabrt不仅在posix中,而且在标准C中。
- @Joachimileborg你也许不能解决所有的问题…最好让程序干净地退出,以防由于意外原因而崩溃。
- "只要消息框不被用户接受,我的应用程序就会继续运行,"-wat
- @mattmcnab clear:除了不接受用户输入之外,因为GUI被阻塞(与模式对话一样)。似乎有些Borland的开发商有这个好主意。
- C++Builder带有RTL和VCL的源代码,所以如果它真的是从那里来的,你应该能够改变行为。不过,我想说的是,这只不过是来自未捕获的非VCL例外。
- 那不是Borland的对话。这是一个系统对话。
- @戴维德芬曼,你确定吗?哪个系统:Windows?(我添加了一个屏幕截图)
- 不,你可能是对的。现在您可以在rtl/vcl源中搜索该文本并找出其原因。
在C++Builder安装文件夹中,检查以下文件:
- sourcecpprtlsourcemiscerrormsg.c-_ErrorMessage的实现
- source cpprtl source process abort.c-abort的实现,它调用_ErrorMessage
- source cpprtl source misc assert.c-_assert的实现,它调用_ErrorMessage
errormsg.c定义了一个未记录的_messagefunc函数指针,可以将其设置为重写默认行为。虽然它是未记录的,并且没有在任何头文件中声明,但是您可以将其声明为extern,并以这种方式访问它。样品使用情况:
1 2 3 4 5 6 7 8 9 10 11 12 13
| extern int (_RTLENTRY * _EXPDATA _messagefunc)(char *msg);
static int LogAndDie(char *msg)
{
LogMessageToSomeFile(msg);
exit(1);
return 0;
}
void InitializeErrorHandling()
{
_messagefunc = LogAndDie;
} |
- 我认为你的建议需要重新考虑。_messagefunc所指的函数只应处理错误消息,即记录错误消息。它是由_ErrorMessage间接调用的,该函数也用于非致命情况。
- @wolf-hmm.我们已经在生产中使用_messagefunc运行了几年的代码(不是像您希望的那样终止,而是使用_messagefunc将错误转化为问题报告以供问题跟踪程序使用)。我在实践中看到的唯一非致命错误是数学错误(例如使用log(0)),这对您的应用程序域来说可能不是问题。无论如何,检查strcmp(msg,"Abnormal program termination") == 0应该足以得到致命的错误。
- 建议的解决方案没有帮助,但是研究的建议有帮助。它帮助我更好地了解发生了什么事。所以我认为这是迄今为止最有帮助的答案。
我可以做一些测试,我只能确认注册sigabrt信号处理程序只是一个noop。
我用一个用VS2008 Express编写的非常简单的GUI应用程序进行了尝试。:
- 没有框架,也没有.NET,只有win-api
- 一个带有退出和致命功能的菜单
- 直接在wndproc中管理的菜单
- 致命执行1/0
结果如下:
- 无特殊操作=>Windows打开一个消息框,指示一个致命错误…
- sigabrt的信号处理程序=>相同的消息框
- C++尝试catch(…)= >同样的MessageBox
- WNDPROC中的SEH:可以拦截错误!
- SEH消息循环:可以截获错误!
如果我使用bot-seh处理程序,则最内部的(wndproc)捕获。
对您来说,好的新功能是,如果足以保护消息循环,那么您就不必进入每个wndproc。
坏消息是我不知道C++ Builder,不能说在哪里找到消息循环。
为了给您一个提示,下面是我如何保护winapi应用程序中的消息循环:
1 2 3 4 5 6 7 8 9 10 11 12 13
| __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一,所以我怀疑C++Builder已经在消息循环中有这样一个异常处理程序。
希望有帮助…
- 谢谢你的研究!C++Builder(6)GUI应用程序是基于VCL框架的。消息循环由Application::Run运行,不受应用程序源的直接影响。我希望更好地遵守这个公约。
- 我曾经(很久很久以前)使用过BC编译器,如果我记得正确的话,它是主应用程序类扩展应用程序,并且可以覆盖很多东西(包括run或…loop)。还是可以这样做吗?
- 这是一个很好的旧Tvision时间,它在Windows下改变了,因为Windows本身是基于一个自己的事件控制的GUI概念。
- 不是很老,Windows3.11次,但它仍在我的记忆中…由于VCL不能公开使用,我只能建议您注意所有可覆盖的方法…
如果要捕获任何程序退出,应查看at exit()。如果要捕获所有终止事件,请查看std::set_terminate(),如果要捕获所有意外的异常,请查看std::set_unexpected()。如果只想捕获abort(),可以使用SIGABRT信号值调用signal()。您还可以使用try{your code}catch(...){custom event handler}包装您的代码。
- 也许我对此还不够清楚:例外并不是我的问题,而是没有例外。
- 这正是我所做的。毫无疑问,这在技术上是可能的。(您答案中的所有其他信息与我的问题无关,因此,抱歉,-1)我想知道,调用此函数时如何正确退出我的程序。我将尝试将此细节添加到问题中。
- @沃尔夫,我第一次问你的问题的时候就回答了,因为我理解了你的问题——从那以后,你对你最初的问题进行了6次编辑。你自己在上面承认你可能不够清楚。
- 我觉得你的回答与问题stackoverflow.com/posts/25305653/timeline的第一个版本不匹配,我在第一个评论中就说过。对于可能出现的编程错误,您的答案非常常见(例如,将其与此答案进行比较)。你不觉得你可以更具体一点吗?
- @Wolf如果你的问题一开始就很清楚,那你为什么要在之后对你的问题进行六次编辑呢?当你自己写得很糟糕的问题才是问题所在的时候,也许你不应该投反对票。
- 您给出的引用声明"当程序正常终止时,func所指的函数会自动无参数地调用。"我的例子是,它是一个异常终止…
- @使用std::set_terminate()而不从终止处理程序函数调用abort()的wolf将防止异常终止。
- 如果我调用abort(),则不会调用此终止处理程序,而是会看到访问冲突(与不调用std::set_terminate的情况相同),我只知道set terminate机制与未捕获的异常结合在一起…
- @Wolf abort()可以触发或不触发atexit()处理程序,您可以尝试。
- 已在控制台应用程序中尝试。throw 0;导致调用我的处理程序(stderr上的my terminate),而abort();在输出中给出了"异常程序终止"。
当未处理的异常导致终止时,您可能可以使用Windows错误报告来创建进程的转储。然后您可以在空闲时查看转储,并允许一些父进程或其他看门狗重新启动您的进程。如果您选择这个策略,您将不会尝试处理代码中的失败,而是允许它。
- 除了Windows错误报告的想法(我还有待探讨):我不认为在这种情况下会有飞行异常。