怎样在一个程序(进程)中捕获它启动的另外一个进程所出现的任何异常?
Windows内部采用了结构化异常处理(SEH)来处理捕获的异常
在VC中,M$将SEH封装为
__try{
}__finally{
}
和
__try{
}__except(DWORD ExceptionProc()){
}
两种
我想用 这种机制来捕获主程序启动的一个子进程的所有异常,写了如下代码(一按钮事件里写的):
————————————————————————————————
STARTUPINFO si;
PROCESS_INFORMATION pi;
si.cb = sizeof(STARTUPINFO);
GetStartupInfo(&si);
si.wShowWindow = SW_HIDE;
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
EXCEPTION_RECORD SavedExceptRec;
char * Dir = "f:\\temp\\sec.exe";
__try
{
RaiseException(1, 0, 0, NULL); //刚开始没加这句
if (!CreateProcess(NULL,Dir,NULL,NULL,TRUE,NULL,NULL,NULL,&si,&pi))
{
MessageBox("Error on CreateProcess()");
return;
}
if ((WaitForSingleObject(pi.hProcess,50000)) == WAIT_TIMEOUT) //控制子进程的运行时间
{
TerminateProcess(pi.hProcess,1);
MessageBox("quit");
return;
}
}
__except (SavedExceptRec = *(GetExceptionInformation())->ExceptionRecord)
{
switch (SavedExceptRec.ExceptionCode)
{
case EXCEPTION_INT_DIVIDE_BY_ZERO:
MessageBox("zero");
break;
case EXCEPTION_INT_OVERFLOW:
MessageBox("overflow");
break;
default:
MessageBox("ddd");
}
}
为了验证
sec程序的源代码如下:
#include <iostream.h>
void main()
{
int a=2,b=0;
int c = a/b;
}
sec.exe是编译好的程序
执行时程序出错,提示发生软件异常。后来在上面加了RaiseException(1, 0, 0, NULL)(软件异常我不太会用) 程序执行了,弹出了MessageBox("ddd")
我不明白,try except不是用来捕获被0除这样的异常的吗?为什么会提示软件异常呢?怎样写才能捕获这样子进程的所有异常呢?
问题点数:70、回复次数:14Top
1 楼In355Hz(好象一条狗)回复于 2005-06-08 17:26:11 得分 40
M$ 的 __try, __expect, __final 只限于处理进程内部,同一个线程内,位于 __try { } block 内部产生的异常。
如果要获得子进程的异常通知,可以使用 DEBUG_PROCESS 标志启动子进程,然后在父进程调用
BOOL WaitForDebugEvent(
LPDEBUG_EVENT lpDebugEvent, // debug event information
DWORD dwMilliseconds // time-out value
);
等待子进程的 DEBUG Event 如果 code 为 EXCEPTION_DEBUG_EVENT 表示子进程出现异常。
Top
2 楼beautifulfly()回复于 2005-06-08 21:36:01 得分 0
In344Hz您好:
你说的这种方法我试过了,在createprocess下加了如下代码:
LPDEBUG_EVENT ss;
DWORD timelength = 1000;
if (WaitForDebugEvent(ss,timelength)!=0) //用 while还是if?
{
switch (ss->dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
MessageBox("exception");
case CREATE_PROCESS_DEBUG_EVENT:
MessageBox("careet");
default:
MessageBox("de");
}
}
去掉了 if ((WaitForSingleObject(pi.hProcess,50000)) == WAIT_TIMEOUT) 这段
但在if (WaitForDebugEvent(ss,timelength)!=0) 这句出错,说内存不能为写,怎么回事呢?
能在我上面的代码中给添加一些代码吗? 谢谢谢谢谢谢!Top
3 楼beautifulfly()回复于 2005-06-08 21:39:08 得分 0
而且,我在一个按钮事件里,单独写了如下程序:
EXCEPTION_RECORD Sc;
__try
{
int x = 0;
double y = 4/x;
}
__except (Sc= *(GetExceptionInformation())->ExceptionRecord)
{
switch (Sc.ExceptionCode)
{
case EXCEPTION_INT_DIVIDE_BY_ZERO:
MessageBox("zero");
break;
case EXCEPTION_INT_OVERFLOW:
MessageBox("overflow");
break;
default:
MessageBox("gg");
}
}
也不能运行 死在那了。 :(Top
4 楼In355Hz(好象一条狗)回复于 2005-06-09 00:57:43 得分 0
DEBUG_EVENT de; // 应该给一个有效的 DEBUG_EVENT 指针给 WaitForDebugEvent
while ( WaitForDebugEvent(&de, INFINITE) != 0 )
{
switch (de.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT
MessageBox("exception");
break;
case CREATE_PROCESS_DEBUG_EVENT:
MessageBox("create");
break;
case EXIT_PROCESS_DEBUG_EVENT:
MessageBox("exit");
break;
default:
MessageBox("de");
}
if (de.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
break;
}Top
5 楼In355Hz(好象一条狗)回复于 2005-06-09 01:04:12 得分 0
EXCEPTION_RECORD * sc;
__try
{
int x = 0;
double y = 4/x;
}
// 这里要利用逗号运算符,因为 __except 的表达式必须返回 EXCEPTION_EXECUTE_HANDLER 表示异常在这里被处理了。
__except (sc= (GetExceptionInformation())->ExceptionRecord, EXCEPTION_EXECUTE_HANDLER)
{
switch (sc.ExceptionCode)
{
case EXCEPTION_INT_DIVIDE_BY_ZERO:
MessageBox("zero");
break;
case EXCEPTION_INT_OVERFLOW:
MessageBox("overflow");
break;
default:
MessageBox("gg");
}
}
Top
6 楼In355Hz(好象一条狗)回复于 2005-06-09 01:05:18 得分 0
写错了一个地方:
switch (sc->ExceptionCode)
{
...
}
Top
7 楼codewarrior(会思考的草)回复于 2005-06-09 08:25:15 得分 0
调试方式启动你要的进程应该就可以了.Top
8 楼codewarrior(会思考的草)回复于 2005-06-09 08:29:16 得分 30
得到一个供调试的程序:
由于我们的程序要扮演调试器的角色,我们还必需要有一个供调试的程序.这个程序可以通过二种方法获得:
1:使用DebugActiveProcess函数.
这个函数的定义是DebugActiveProcess(dwProcessID: DWORD):Bool; stdcall, dwProcessID用于指定被调试的进程的标识符,如果函数调用成功返回TRUE,失败返回FALSE.注意,如果是在NT/2000/XP上,如果目标进程是由一个安全描述器创建的,而该安全描述符使调试器没有充分的访问权,那么此函数的调用可能失败.
2:使用CreateProcess函数.
这个函数的定义是CreateProcess(lpApplicationName: PChar; lpCommandLine: PChar;lpProcessAttributes, lpThreadAttributes: PSecurityAttributes;
bInheritHandles: BOOL; dwCreationFlags: DWORD; lpEnvironment: Pointer;
lpCurrentDirectory: PChar; const lpStartupInfo: TStartupInfo;
var lpProcessInformation: TProcessInformation): BOOL; stdcall
由于篇幅原因,这儿就不详解CreateProcess的每个参数的含义,具体请参考API大全,我们这儿只谈如何创建一个被调试的进程.即设置dwCreationFlags参数,你可以指定DEBUG_PROCESS标志来建立一个被调试进程,同时被调试进程的子进程的调试信息也将通知我们的调试器.还可以指定DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS标志来表示只调试当前过程.
处理调试信息:
当我们用上面的方法之一打开了被调试的程序后,我们的程序应调用WaitForDebugEvent等待处理调试事件.它阻塞调用线程直到调试的事件发生.此函数的定义是:
WaitForDebugEvent(var lpDebugEvent: TDebugEvent; dwMilliseconds: DWORD): BOOL; stdcall;
其中lpDebugEvent结构将在调试事件发生时返回发生的调试事件信息.dwMilliseconds值指定函数等待调试事件的时间,以毫秒为单位,一般设为INFINITE,表示一直等待直到调试事件发生.
这有点于类似于一个消息循环.我们一般都会新建一个线程,在线程中使用DebugActiveProcess或CreateProcess得到一个供调试的程序,然后用一个循环调用WaitForDebugEvent来处理随后发生的调试事件.至于为什么要在新的线程中处理呢?你不会想你的调试器一打开被调试程序后就一动也不能动了吧 ;-)
继续运行被调试程序:
当调试事件发生后,被调试程序会被WINDOWS挂起,当我们处理完了调试事件后,还要让被调试程序继续运行,这就要用到ContinueDebugEvent函数,定义如下:
ContinueDebugEvent(dwProcessId, dwThreadId, dwContinueStatus: DWORD): BOOL; stdcall;
其中dwProcessID和dwThreadID是要被恢复的进程和线程ID,可以从lpDebugEvent结构中的dwProcessID和dwThreadID取得.dwContinueStatus是指明如何恢复线程,可能的取值有DBG_CONTINUE 和DBG_EXCEPTION_NOT_HANDLED,DBG_CONTINUE指明了如果被调试程序发生了异常,由调试器来处理异常.DBG_EXCEPTION_NOT_HANDLED则表示调试器不处理被调试程序的异常,由被调试程序的默认异常处理程序来处理异常.Top
9 楼codewarrior(会思考的草)回复于 2005-06-09 08:30:58 得分 0
Win32有一些供程序员使用的API,它们提供相当于调试器的功能. 他们被称作Win32调试API(或原语).利用这些API,我们可以:
加载一个程序或捆绑到一个正在运行的程序上以供调试
获得被调试的程序的低层信息,例如进程ID,进入地址,映像基址等.
当发生与调试有关的事件时被通知,例如进程/线程的开始/结束, DLL的加载/释放等.Top
10 楼beautifulfly()回复于 2005-06-09 09:08:17 得分 0
那就是说要捕获主进程用createprocess产生的子进程所出现的任何异常,只能用调试这样方法了?Top
11 楼beautifulfly()回复于 2005-06-09 10:23:40 得分 0
我按您说的写了:
DEBUG_EVENT de;
while (WaitForDebugEvent(&de, INFINITE)!=0)
{
if (de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT)
MessageBox("create");
if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
{
switch (de.u.Exception.ExceptionRecord.ExceptionCode)
{
case EXCEPTION_INT_DIVIDE_BY_ZERO:
MessageBox("zero");
break;
case EXCEPTION_INT_OVERFLOW:
MessageBox("INT");
break;
case EXCEPTION_ACCESS_VIOLATION:
MessageBox("ACCESS");
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
MessageBox("DATATYPE");
break;
case EXCEPTION_FLT_STACK_CHECK:
MessageBox("DATATYPE");
break;
case EXCEPTION_INVALID_DISPOSITION:
MessageBox("DISPOSITION");
break;
case EXCEPTION_STACK_OVERFLOW:
MessageBox("OVERFLOW");
break;
default:
MessageBox("exception");
}
TerminateProcess(pi.hProcess,1);
}
if (de.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
{
MessageBox("exit");
break;
}
ContinueDebugEvent(pi.dwProcessId,pi.dwThreadId,DBG_CONTINUE);
}
可以运行了
但怎么捕捉不到被0除得提示呢?总是提示 MessageBox("exception")这句,我怎么才能捕捉到被0除得异常呢?
Top
12 楼codewarrior(会思考的草)回复于 2005-06-09 10:48:00 得分 0
被0除的异常是肯定可以捕获到的,可能你用的不是很正确吧...Top
13 楼beautifulfly()回复于 2005-06-09 11:44:34 得分 0
我启动的子进程就是故意写的被0除,上面的代码也判断了,难道第一个产生的异常不是被0除?
能指出错在那里吗?
Top
14 楼beautifulfly()回复于 2005-06-10 11:13:29 得分 0
发现不能监测到EXCEPTION_INT_DIVIDE_BY_ZERO的原因是:无论子进程正确与否,总是要先抛出EXCEPTION_BREAKPOINT 异常,然后再弹出其他异常。EXCEPTION_BREAKPOINT 是什么原因弹出的异常呢?Top




