【问题标题】:Best way to detect an application crash and restart it?检测应用程序崩溃并重新启动它的最佳方法?
【发布时间】:2010-09-09 20:00:53
【问题描述】:

在 XP 中检测应用程序崩溃的最佳方法是什么(每次都产生相同的“错误”窗口对 - 每个窗口标题相同)然后重新启动它?

我对使用最少系统资源的解决方案特别感兴趣,因为所讨论的系统已经很老了。

我曾想过使用像 AutoIt (http://www.autoitscript.com/autoit3/) 这样的脚本语言,并且可能每隔几分钟触发一个“检测器”脚本?

在 Python、Perl、PowerShell 或完全其他的东西中这样做会更好吗?

非常感谢任何想法、提示或想法。

编辑:它实际上并没有崩溃(即退出/终止 - 感谢@tialaramex)。它显示一个等待用户输入的对话框,然后是另一个等待用户进一步输入的对话框,然后它实际上退出了。我想检测和处理的正是这些对话框。

【问题讨论】:

  • 应用程序是真的崩溃(退出、终止、停止运行)还是显示错误对话框并等待你做某事?
  • 这是什么错误?发送给 Microsoft 的,还是一些自定义的?

标签: windows-xp crash


【解决方案1】:

最好的方法是使用命名mutex

  1. 启动您的应用程序。
  2. 创建一个新的命名互斥体并取得其所有权
  3. 启动新进程(进程不是线程)或新应用程序,随心所欲。
  4. 尝试从该进程/应用程序获取互斥锁。该进程将阻塞
  5. 当应用程序完成释放互斥体(信号)
  6. “控制”进程仅在应用程序完成或应用程序崩溃时才会获取互斥锁。
  7. 在获取互斥锁后测试结果状态。如果应用程序崩溃了,它将是 WAIT_ABANDONED

解释:当一个线程完成而没有释放互斥锁时,任何其他等待它的进程都可以获取它,但它会获得一个 WAIT_ABANDONED 作为返回值,这意味着互斥锁被放弃,因此状态为受保护的部分可能不安全。

这样,您的第二个应用程序将不会消耗任何 CPU 周期,因为它会一直等待互斥锁(并且由操作系统认真处理)

【讨论】:

    【解决方案2】:

    这是一个稍微改进的版本。

    在我的测试中,当错误的 exe 生成“访问冲突”时,之前的代码在无限循环中运行。

    我对我的解决方案并不完全满意,因为我没有明确的标准来知道哪个异常应该继续,哪个不能继续(ExceptionFlags 没有帮助)。

    但它适用于我运行的示例。

    希望对你有帮助, 薇薇安·德·史密特

    from ctypes import windll, c_uint, c_void_p, Structure, Union, pointer
    import subprocess
    
    WaitForDebugEvent = windll.kernel32.WaitForDebugEvent
    ContinueDebugEvent = windll.kernel32.ContinueDebugEvent
    DBG_CONTINUE = 0x00010002L
    DBG_EXCEPTION_NOT_HANDLED = 0x80010001L
    
    event_names = {
        1: 'EXCEPTION_DEBUG_EVENT',
        2: 'CREATE_THREAD_DEBUG_EVENT',
        3: 'CREATE_PROCESS_DEBUG_EVENT',
        4: 'EXIT_THREAD_DEBUG_EVENT',
        5: 'EXIT_PROCESS_DEBUG_EVENT',
        6: 'LOAD_DLL_DEBUG_EVENT',
        7: 'UNLOAD_DLL_DEBUG_EVENT',
        8: 'OUTPUT_DEBUG_STRING_EVENT',
        9: 'RIP_EVENT',
    }
    
    EXCEPTION_MAXIMUM_PARAMETERS = 15
    
    EXCEPTION_DATATYPE_MISALIGNMENT  = 0x80000002
    EXCEPTION_ACCESS_VIOLATION       = 0xC0000005
    EXCEPTION_ILLEGAL_INSTRUCTION    = 0xC000001D
    EXCEPTION_ARRAY_BOUNDS_EXCEEDED  = 0xC000008C
    EXCEPTION_INT_DIVIDE_BY_ZERO     = 0xC0000094
    EXCEPTION_INT_OVERFLOW           = 0xC0000095
    EXCEPTION_STACK_OVERFLOW         = 0xC00000FD
    
    
    class EXCEPTION_DEBUG_INFO(Structure):
        _fields_ = [
            ("ExceptionCode", c_uint),
            ("ExceptionFlags", c_uint),
            ("ExceptionRecord", c_void_p),
            ("ExceptionAddress", c_void_p),
            ("NumberParameters", c_uint),
            ("ExceptionInformation", c_void_p * EXCEPTION_MAXIMUM_PARAMETERS),
        ]
    
    class EXCEPTION_DEBUG_INFO(Structure):
        _fields_ = [
            ('ExceptionRecord', EXCEPTION_DEBUG_INFO),
            ('dwFirstChance', c_uint),
        ]
    
    class DEBUG_EVENT_INFO(Union):
        _fields_ = [
            ("Exception", EXCEPTION_DEBUG_INFO),
        ]
    
    class DEBUG_EVENT(Structure):
        _fields_ = [
            ('dwDebugEventCode', c_uint),
            ('dwProcessId', c_uint),
            ('dwThreadId', c_uint),
            ('u', DEBUG_EVENT_INFO)
        ]
    
    def run_with_debugger(args):
        proc = subprocess.Popen(args, creationflags=1)
        event = DEBUG_EVENT()
    
        num_exception = 0
    
        while True:
            if WaitForDebugEvent(pointer(event), 10):
                print event_names.get(event.dwDebugEventCode, 'Unknown Event %s' % event.dwDebugEventCode)
    
                if event.dwDebugEventCode == 1:
                    num_exception += 1
    
                    exception_code = event.u.Exception.ExceptionRecord.ExceptionCode
    
                    if exception_code == 0x80000003L:
                        print "Unknow exception:", hex(exception_code)
    
                    else:
                        if exception_code == EXCEPTION_ACCESS_VIOLATION:
                            print "EXCEPTION_ACCESS_VIOLATION"
    
                        elif exception_code == EXCEPTION_INT_DIVIDE_BY_ZERO:
                            print "EXCEPTION_INT_DIVIDE_BY_ZERO"
    
                        elif exception_code == EXCEPTION_STACK_OVERFLOW:
                            print "EXCEPTION_STACK_OVERFLOW"
    
                        else:
                            print "Other exception:", hex(exception_code)
    
                        break
    
                ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_CONTINUE)
    
            retcode = proc.poll()
            if retcode is not None:
                return retcode
    
    run_with_debugger(['crash.exe'])
    

    【讨论】:

      【解决方案3】:

      我认为主要问题是 Watson 博士显示了一个对话框 并使您的流程保持活力。

      您可以使用 Windows API 编写自己的调试器,并且 从那里运行崩溃的应用程序。 这将防止其他调试器捕获崩溃 您的应用程序,您还可以捕获异常事件。

      由于我没有找到任何示例代码,所以我写了这个 Python 快速而肮脏的示例。我不确定它有多强大 特别是 DEBUG_EVENT 的声明可以改进。

      from ctypes import windll, c_int, Structure
      import subprocess
      
      WaitForDebugEvent = windll.kernel32.WaitForDebugEvent    
      ContinueDebugEvent = windll.kernel32.ContinueDebugEvent
      DBG_CONTINUE = 0x00010002L    
      DBG_EXCEPTION_NOT_HANDLED = 0x80010001L
      
      event_names = {    
          3: 'CREATE_PROCESS_DEBUG_EVENT',
          2: 'CREATE_THREAD_DEBUG_EVENT',
          1: 'EXCEPTION_DEBUG_EVENT',
          5: 'EXIT_PROCESS_DEBUG_EVENT',
          4: 'EXIT_THREAD_DEBUG_EVENT',
          6: 'LOAD_DLL_DEBUG_EVENT',
          8: 'OUTPUT_DEBUG_STRING_EVENT', 
          9: 'RIP_EVENT',
          7: 'UNLOAD_DLL_DEBUG_EVENT',
      }
      class DEBUG_EVENT(Structure):
          _fields_ = [
              ('dwDebugEventCode', c_int),
              ('dwProcessId', c_int),
              ('dwThreadId', c_int),
              ('u', c_int*20)]
      
      def run_with_debugger(args):
          proc = subprocess.Popen(args, creationflags=1)
          event = DEBUG_EVENT()
      
          while True:
              if WaitForDebugEvent(pointer(event), 10):
                  print event_names.get(event.dwDebugEventCode, 
                          'Unknown Event %s' % event.dwDebugEventCode)
                  ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_CONTINUE)
              retcode = proc.poll()
              if retcode is not None:
                  return retcode
      
      run_with_debugger(['python', 'crash.py'])
      

      【讨论】:

        【解决方案4】:

        我知道您使用的是 Windows XP,但对于在 Vista 下处于类似情况的人,有新的crash recovery APIs 可用。 Here's a good introduction 他们能做什么。

        【讨论】:

          【解决方案5】:

          如何创建一个包装应用程序,将故障应用程序作为孩子启动并等待它?如果子进程的退出代码指示错误,则重新启动它,否则退出。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-02-23
            • 1970-01-01
            • 2023-03-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多