【问题标题】:Make main() "uncrashable"使 main() “不可崩溃”
【发布时间】:2011-09-08 16:54:53
【问题描述】:

我想编写一个守护进程管理器来确保所有守护进程都在运行,就像这样(简化的伪代码):

void watchMe(filename)
{
    while (true)
    {
        system(filename); //freezes as long as filename runs
        //oh, filename must be crashed. Nevermind, will be restarted            
    }
}       

int main()
{
    _beginThread(watchMe, "foo.exe");
    _beginThread(watchMe, "bar.exe");
}

这部分已经在工作 - 但现在我面临的问题是,当观察到的应用程序 - 比如 foo.exe - 崩溃时,相应的系统调用会冻结,直到我确认这个漂亮的消息框:

这使得守护进程无用。

我认为可能的解决方案是使观察到的程序(我控制)的 main() “不可崩溃”,以便它们优雅地关闭而不显示这个丑陋的消息框。

像这样:

try
{
    char *p = NULL;
    *p = 123; //nice null pointer exception
}
catch (...)
{
    cout << "Caught Exception. Terminating gracefully" << endl;
    return 0;
}

但这不起作用,因为它仍然会产生此错误消息:

("未处理的异常...写访问冲突...")

我已经尝试过 SetUnhandledExceptionFilter 和所有其他东西,但没有效果。

任何帮助将不胜感激。

问候

【问题讨论】:

  • 空指针取消引用不会显式导致异常。
  • 这正是我的 VS2005 设置所发生的事情。但无论如何,这只是导致应用程序崩溃的代码示例。
  • @David:如果您想要一个异常示例,只需抛出一个。 :) throw 666;
  • 如果我这样做,我可以捕获异常。我的问题是,即使我将代码包装在 try/catch 中,我对应用程序崩溃也无能为力。
  • 该程序依赖于一些我无法保证的库。此外,程序必须“永远”运行,你永远不会知道 - 所以我最好小心。

标签: c++ exception-handling


【解决方案1】:

我很久以前就这样做了(在 90 年代,在 NT4 上)。我预计这些原则不会改变。

基本方法是在您开始注入一个 DLL 的过程后,该 DLL 复制了 KERNEL32.DLL 中的 UnhandledExceptionFilter() 的功能。翻遍我的旧代码,我发现我修补了 GetProcAddress、LoadLibraryA、LoadLibraryW、LoadLibraryExA、LoadLibraryExW 和 UnhandledExceptionFilter。

LoadLibrary* 函数的挂钩处理确保所有模块都存在修补程序。修改后的 GetProcAddress 提供了函数补丁版本的地址,而不是 KERNEL32.DLL 版本。

当然,UnhandledExceptionFilter() 替代品可以满足您的需求。例如,启动一个即时调试器来进行进程转储(核心转储在 NT 和后续系统的用户模式下实现),然后终止该进程。

我的实现使用 __declspec(naked) 实现了修补函数,并手动处理了所有注册,因为编译器可以破坏一些寄存器的内容,这些寄存器的调用者可能不会从程序集中被破坏。

当然还有很多细节,但这是基本的大纲。

【讨论】:

    【解决方案2】:

    您可以异步运行被监视的进程,并使用内核对象与其通信。例如,您可以:

    1. Create 一个命名事件。
    2. Start 目标进程。
    3. Wait 创建的事件
    4. 在目标进程中,遇到崩溃时,打开命名事件,set it

    这样,一旦在被监视的进程中遇到崩溃,你的监视器就会继续运行,即使被监视的进程还没有结束。

    顺便说一句,您也许可以使用 drwtsn32(或 Win7 中使用的任何东西)控制第一条错误消息的外观,我不确定,但第二条错误消息可能只出现在调试版本中。在发布模式下构建可能会让您更容易,尽管最重要的是,恕我直言,首先解决崩溃的原因 - 这在调试构建中会更容易。

    【讨论】:

    • 感谢您的回答。但问题依然存在。即使我使用 CreateProcess,我愚蠢的空指针取消引用也会产生同样愚蠢的消息框(“应用程序已终止......”)。
    • @David Müller - 我的建议不会阻止消息出现,但至少你知道守护进程已经崩溃,并且可以运行另一个实例。要禁用消息本身,请查看此处的建议:stackoverflow.com/questions/735170/…
    【解决方案3】:

    您可以在编译器命令行中将 /EHsc 更改为 /EHa 标志(属性/C/C++/代码生成/启用 C++ 异常)。

    有关 SO 的类似问题,请参阅 this

    【讨论】:

      【解决方案4】:

      这似乎更像是SEH 异常而不是 C++ 异常,需要以不同方式处理,请尝试以下代码:

      __try
      {
          char *p = NULL;
          *p = 123; //nice null pointer exception
      }
      __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? 
                   EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
      {
          cout << "Caught Exception. Terminating gracefully" << endl;
          return 0;
      }
      

      但那是一种补救措施而不是治愈方法,您可能会在 sandbox 中运行这些进程更好。

      【讨论】:

        猜你喜欢
        • 2013-08-08
        • 2022-10-21
        • 1970-01-01
        • 1970-01-01
        • 2016-02-18
        • 1970-01-01
        • 2016-06-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多