题
在 XP 中检测应用程序崩溃(每次生成同一对“错误”窗口 - 每个窗口具有相同的窗口标题)然后重新启动的最佳方法是什么?
我特别有兴趣听到使用最少系统资源的解决方案,因为相关系统相当旧。
我曾想过使用像 AutoIt 这样的脚本语言(http://www.autoitscript.com/autoit3/),也许每隔几分钟触发一个“检测器”脚本?
使用 Python、Perl、PowerShell 或者完全其他的东西来完成这个会更好吗?
非常感谢任何想法、提示或想法。
编辑:它实际上并没有崩溃(即退出/终止 - 感谢@tialaramex)。它显示一个等待用户输入的对话框,然后是另一个等待用户进一步输入的对话框,然后它实际上退出。我想要检测和处理的正是这些对话框。
解决方案
创建一个包装应用程序来将有问题的应用程序作为子应用程序启动并等待它怎么样?如果子进程的退出代码指示错误,则重新启动它,否则退出。
其他提示
最好的方法是使用命名的 互斥量.
- 开始您的应用程序。
- 创建一个新的命名互斥体并取得它的所有权
- 启动一个新进程(进程而不是线程)或新应用程序,无论您喜欢什么。
- 从该进程/应用程序尝试获取互斥体。进程会阻塞
- 当应用程序完成时释放互斥体(向其发出信号)
- 仅当应用程序完成或应用程序崩溃时,“控制”进程才会获取互斥锁。
- 获取互斥体后测试结果状态。如果应用程序崩溃了,它将处于 WAIT_ABANDONED 状态
解释: 当线程完成而没有释放互斥体时,等待它的任何其他进程都可以获取它,但它将获得 WAIT_ABANDONED 作为返回值,这意味着互斥体被放弃,因此它所保护的部分的状态可能是不安全的。
这样,您的第二个应用程序就不会消耗任何 CPU 周期,因为它将继续等待互斥体(并且由操作系统轻松处理)
我认为主要问题是博士。沃森(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'])
我知道您正在处理 Windows XP,但对于 Vista 下遇到类似情况的人来说,有新的 崩溃恢复API可用。 这里有一个很好的介绍 他们能做什么。
这是一个稍微改进的版本。
在我的测试中,当有问题的 exe 生成“访问冲突”时,前面的代码会无限循环运行。
我对我的解决方案并不完全满意,因为我没有明确的标准来知道哪个异常应该继续,哪个异常不能继续(ExceptionFlags 没有帮助)。
但它适用于我运行的示例。
希望它能有所帮助,Vivian de Smedt
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'])