原文链接:http://hi.baidu.com/71167609/blog/item/fe6107ca33074853f31fe711.html
程序中存在未处理的异常时,进程的默认异常处理函数通常会显示一个对话框,询问用户是关闭程序还是调试程序。比如,在2000的系统上,这个提示框如下图(在XP,Vista,Win7上界面会不一样)。这个提示框是线程的默认处理函数UnhandledExceptionFilter()中弹出的。对于程序员来说,这个提示框很友好,告诉你出了什么问题。点击取消,还可以立即调试。但是对于普通用户来说,就不那么友好了,有些用户可能会认为是他的系统出问题了。因此,需要一些方法,来屏蔽这个异常提示框,或者是换成我们自己更友好的提示框。
如果这个程序是自己写的,熟悉Windows异常处理机制的话,就应该想到一些方法可以把这个异常提示框给去掉。比如通过SetErrorMode,或者自己通过try-except块来自己处理这个异常。
但是,如果这个程序不是自己写的,如何处理。今天碰到了这样一个问题,这个问题产生的场景如下,有几千个手机程序的安装包在Windows上通过一个A.exe程序来处理,这个A.exe是通过perl脚本来调用的。正常情况下,期望这个脚本能够不需要人工干预,处理这几千个安装包,但是有几个安装包会导致这个A.exe程序出现异常 ,需要手工点击一下系统默认的异常处理弹出的对话框后,perl脚本才能继续执行。希望能够忽略这个异常,出错后继续转到下一个去执行。
这个问题的麻烦之处在于,这个用来解包的exe是一个第三方的软件,没有源码,不可能自己去修改。这个问题首先想到的方法是修改一下exe,加入一条对SetErrorMode调用的汇编码,反汇编看了一下后,放弃了这个打算,因为没有找到一个合适的插入的位置。想到了另外一种方式。当一个进程在调试器中运行时,异常会先给调试器来处理。而Windows又提供了一套API,可以以调试模式来启动一个进程,在父进程中,可以处理来自子进程的异常。
因此,解决这个问题的一个办法是,实现另一个B.exe,Perl脚本中调用A.exe的地方,全部改成B.exe。但是参数保持不变。B.exe中干的活就是以调试模式创建A.exe,以一个循环来处理A.exe中的异常,收到进程结束的异常后,就可以退出循环,退出A.exe进程了。
关键的API如下:
CreateProcess(),DEBUG_PROCESS
WaitForDebugEvent,ContinueDebugEvent。
参考下面的例子:
STARTUPINFO st = {0};
PROCESS_INFORMATION pro = {0};
st.cb = sizeof(st);
CreateProcess(NULL, "d:\\test.exe", NULL, NULL, TRUE,
DEBUG_ONLY_THIS_PROCESS,
NULL, NULL, &st, &pro);
CloseHandle(pro.hThread);
CloseHandle(pro.hProcess);
DEBUG_EVENT dbe;
BOOL rc;
while(WaitForDebugEvent(&dbe, INFINITE))
{
if(dbe. dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
break;
ContinueDebugEvent(dbe.dwProcessId , dbe.dwThreadId , DBG_CONTINUE );
}
需要调试权限:
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount= 1;
DWORD dwRetLen= 0;
LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);// 查找当前权限
tp.Privileges[0].Attributes= SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof(tp), NULL, &dwRetLen);//提升至SetDebugPrivilege权限