CSDN首页 空间 新闻 论坛 Blog 下载 读书 网摘 搜索 .NET Java 视频 接项目 求职 在线学习 买书 程序员 通知
不看会后悔的Windows XP之经验谈 简单快捷DIY实用家庭影院
CSDN社区
搜索 收藏 打印 关闭
CSDN社区 >  VC/MFC >  进程/线程/DLL

怎样在一个程序(进程)中捕获它启动的另外一个进程所出现的任何异常?

楼主beautifulfly()2005-06-08 15:07:38 在 VC/MFC / 进程/线程/DLL 提问

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

相关问题

  • 用ApplicationEvents如何捕获程序异常?
  • 异常捕获
  • “未安装打印机”的异常在程序中什么地方捕获?
  • 【菜鸟问题】捕获了异常后,能不能不要退出程序啊????
  • 有关异常的捕获!
  • 怎样捕获异常?
  • 异常捕获的问题
  • 急问try catch里捕获了异常之后怎么才能不退出程序?
  • 有什么办法可以在Delphi调试情况下,让程序先捕获异常,而不是Delphi先捕获异常!否则,一但出现异常就没法调试了!!!
  • 我写的一个C#程序会没有任何征兆的情况下退出,而且在可能发生异常的语句没有捕获到异常

关键词

  • win32
  • 函数
  • debug
  • 代码
  • 进程
  • messagebox
  • 异常
  • 捕获
  • dwdebugeventcode
  • 程序

得分解答快速导航

  • 帖主:beautifulfly
  • In355Hz
  • codewarrior

相关链接

  • Visual C++类图书
  • Visual C++类源码下载

广告也精彩

反馈

请通过下述方式给我们反馈
反馈
提问
网站简介|广告服务|VIP资费标准|银行汇款帐号|网站地图|帮助|联系方式|诚聘英才|English|问题报告
北京创新乐知广告有限公司 版权所有, 京 ICP 证 070598 号
世纪乐知(北京)网络技术有限公司 提供技术支持
Copyright © 2000-2008, CSDN.NET, All Rights Reserved
GongshangLogo