从失败终止到崩溃
一个失败的终止
撞车事故时有发生。任何比“Hello world”更复杂的程序都可能有一些bug。专业软件开发的一个衡量标准是如何处理这些崩溃。程序应该保存一个崩溃转储,然后自杀(TerminateProcess()或_exit(),而不是ExitProcess()或exit())。
你不想让这个注定要失败的进程弹出一个对话框,说“嘿,我是一个注定要失败的进程”。但不幸的是,这是VisualC++C运行时间(VC++)在某些情况下所做的,正如我们看到的那样。
如果您不小心调用了一个纯虚拟函数,则此处理程序将打开一个对话框。如果你是一个开发人员,那么你可以附加一个调试器并获得一个调用堆栈,但世界上大多数人都不是开发人员。他们不知道什么是纯虚函数调用,也不在乎。显示此对话框只会减慢崩溃恢复过程,同时让用户感到困惑。
但更糟糕的是。如果你有一群异常处理程序准备捕捉Win32异常(访问冲突等),那么你会失望的,因为他们不会捕捉到纯粹的调用错误,即使有人按了OK。所以,你的内部崩溃转储记录系统对这个错误无能为力,这意味着它需要更长的时间来修复。
更更糟糕的是,如果这个错误发生在服务器上(我见过),那么您的无头服务器现在有一个挂起的进程正在等待某人单击“确定”。单元测试最终会超时,如果您有看门狗,服务器可能会超时,但是整个过程会被这个对话框延迟。
除非我能提供解决方案,否则我不会写这篇文章。上面的对话框是默认行为,但是一旦您知道应该更改默认值,就很容易了。您所要做的就是使用一个故意崩溃的函数调用_set_purecall_handler()。我最喜欢的实现是在TerminateProcess()后面执行一个_debugbreak()。如果我在调试器下运行,这会很好地将我放入其中,如果我不是,那么我未处理的异常过滤器将捕捉异常并写出一个小转储。TerminateProcess()用于阻止在调试器中捕获异常的用户尝试继续。
无效参数在技术上不是崩溃
VC++CRT检测到CRT函数的一些无效参数,并将它们视为致命错误。如果您使用更安全的CRT函数(并且您没有请求截断),这包括缓冲区溢出检测,但是触发这些检查的最简单方法是使用“printf(NULL);”。
不会弹出任何对话框(至少在发布版本中没有),并且进程被终止,但不会通过调用精心编制的异常处理程序来终止。Windows错误报告(WER)会收到问题的通知,这很好,但我希望这些无效参数像崩溃一样处理,以便调用异常处理程序。幸运的是,这个问题也有一个简单的解决方案。如果调用_set_invalid_parameter_handler(),则可以为它提供与纯调用处理程序相同的代码(只是带有不同的签名),以便异常处理程序注意到发生了错误。现在你的程序会比以前更糟糕。这是件好事。示例代码中也演示了这种技术。
WER是你的朋友
Windows错误报告(WER)是Windows内置的一个方便的功能。大多数开发人员都知道WER会在数百万用户的计算机上记录崩溃转储并将其存储起来,而且大多数开发人员也知道可以访问您的软件的崩溃转储。这是一个很好的方法,可以找出你的软件在实际客户的实际机器上实际崩溃的地方。虽然有一些困难需要克服,但还是值得一试的。不过,我不知道如何安排这样的访问,所以我就不多说了。
- 上一篇: Python爬虫常用正则re.findall的使用
- 下一篇: 调试器不应该改变行为