zoukankan      html  css  js  c++  java
  • Windows核心编程 第2 5章 未处理异常和C ++异常(上)

    未处理异常和C + +异常(上)

        前一章讨论了当一个异常过滤器返回 E X C E P T I O N _ C O N T I N U E _ S E A R C H时会发生什么事情。返回EXCEPTION_CONTINUE_SEARCH 是告诉系统继续上溯调用树,去寻找另外的异常过滤器。但是当每个过滤器都返回E X C E P T I O N _ C O N T I N U E _ S E A R C H时会出现什么情况呢?在这种情况下,就出现了所谓的“未处理异常”(Unhandled exception)。

    在第6章里我们已经知道,每个线程开始执行,实际上是利用 K e r n e l 3 2 . d l l中的一个函数来调用B a s e P r o c e s s S t a r tB a s e T h r e a d S t a r t。这两个函数实际是一样的,区别在于一个函数用于进程的主线程(Primary thread):


    另一个函数用于进程的所有辅助线程(Secondary thread):


        注意这两个函数都包含一个S E H框架。每个函数都有一个t r y块,并从这个t r y块里调用主线程或辅助线程的进入点函数。所以,当线程引发一个异常,所有过滤器都返回 E X C E P T I O N _C O N T I N U E _ S E A R C H时,将会自动调用一个由系统提供的特殊过滤器函数: U n h a n d l e dE x c e p t i o n F i l t e r


        这个函数负责显示一个消息框,指出有一个进程的线程存在未处理的异常,并且能让用户

    结束或调试这个进程。

    下面是写个测试代码来看下:

    测试代码,引发异常,但是没有用__except去处理。根据上面的那个创建进程的函数,会先调用U n h a n d l e dE x c e p t i o n F i l t e r弹个窗,点击关闭,然后全局展开,执行finally的代码。


    25.1 即时调试

        随时将调试程序连接到任何进程的能力称为即时调试(Just-in-time Debugging)。这里我们对它如何工作稍加说明:当程序员点击 C a n c e l按钮,就是告诉U n h a n d l e d E x c e p t i o n F i l t e r函数对进程进行调试。在内部,U n h a n d l e d E x c e p t i o n F i l t e r调用调试程序,这需要查看下面的注册表子关键字:

    在这个子关键字里,?有一个名为D e b u g g e r的数值,在安装Visual Studio时被设置成下面的值:


        这一行代码是告诉系统要将哪一个程序(这里是 M S D e v. ? e x e)作为调试程序运行。当然也可以选择其他调试程序。U n h a n d l e d E x c e p t i o n F i l t e r还在这个命令行中向调试程序传递两个参数。第一个参数是被调试进程的 I D。第二个参数规定一个可继承的手工复位事件,这个事件是由U n h a n d l e d E x c e p t i o n F i l t e r按无信号状态建立的。厂商必须实现他们的调试程序,这样才能认识指定进程I D和事件句柄的- p- e选项。

         在进程 I D和事件句柄都合并到这个串中之后, U n h a n d l e d E x c e p t i o n F i l t e r通过调用C r e a t e P r o c e s s来执行调试程序。?这时,调试程序进程开始运行并检查它的命令行参数。如果存在- p选项,调试程序取得进程I D,并通过调用D e b u g A c t i v e P r o c e s s将自身挂接在该进程上。

    BOOL DebugActiveProcess(DWORD dwProcessID);

       一旦调试程序完成自身的挂接,操作系统将被调试者(d e b u g g e e)的状态通报给调试程序。例如,系统将告诉调试程序,在被调试的进程中有多少线程?哪些 D D L加载到被调试进程的地址空间中?调试程序需要花时间来积累这些数据,以准备调试进程。在这些准备工作进行的时候,U n h a n d l e d E x c e p t i o n F i l t e r中的线程必须等待。为此,这要调用 Wa i t F o r S i n g l e O b j e c t函数并传递已经建立的手工复位事件的句柄作为参数。这个事件是按无信号状态建立起来的,所以被调试进程的线程要立即被挂起以等待事件。

        在调试程序完全初始化之后,它要再检查它的命令行,找 - e选项。如果该选项存在,调试程序取得相应的事件句柄并调用 S e t E v e n t。调试程序可以直接使用事件的句柄值,因为事件句柄具有创建的可继承性,并且被调试进程对 U n h a n d l e d E x c e p t i o n F i l t e r函数的调用也使调试程序进程成为一个子进程。设定这个事件将唤醒被调试进程的线程。被唤醒的线程将有关未处理异常的信息传递给调试程序。调试程序接收这些通知并加载相应的源代码文件,再将自身放在引发异常的指令位置上。

        还有,不必在调试进程之前等待异常的出现。可以随时将一个调试程序连接在任何进程上,只需运行“MSDEV -p PID”,其中P I D是要调试的进程的 I D。实际上,利用 Windows 2000Task Manager,做这些事很容易。当观察P r o c e s s标记栏时,可以选择一个进程,点击鼠标右键,并选择D e b u g菜单选项。这将引起 Task Manager去查看前面讨论过的注册表子关键字,调用C r e a t e P r o c e s s,并传递所选定的进程的 I D作为参数。在这里,Task Manager为事件句柄传送0值。

    尝试了下那个函数,结果如下:


    25.2 关闭异常消息框

        有时候,在异常发生时,你可能不想在屏幕上显示异常消息框。例如,你可能不想让这些消息框出现在你产品的发售版本中。如果出现了消息框,很容易导致最终用户意外地启动调试程序来调试你的程序。最终用户只需点击一下消息框中的 C a n c e l按钮,就进入了不熟悉的、令人恐惶的区域 — 调试程序。可以使用几种不同的方法来防止这种消息框的出现。


    25.2.1 强制进程终止运行

    为防止U n h a n d l e d E x c e p t i o n F i l t e r显示异常消息框,可以调用下面的S e t E r r o r M o d e l函数,并向它传递一个S E M _ N O G P FA U LT E R R O R B O X标识符:

    UINT SetErrorMode(UINT fuErrorMode);

    然后,当调用U n h a n d l e d E x c e p t i o n F i l t e r函数来处理异常时,看到已经设置了这个标志,就会立即返回E X C E P T I O N _ E X E C U T E _ H A N D L E R。这将导致全局展开并执行B a s e P r o c e s s S t a r tB a s e T h r e a d S t a r t中的处理程序。该处理程序结束进程

    主线程里异常不会崩溃


    在其他线程里,也不会崩溃,相当于这个设置是整个进程的。

     

    25.2.2 包装一个线程函数

        使用另外一种办法也可以避免出现这个消息框,就是针对主线程进入点函数( m a i n

    w m a i nWi n M a i nw Wi n M a i n)的整个内容安排一个t r y - e x c e p t块。保证异常过滤器的结果值总是E X C E P T I O N _ E X E C U T E _ H A N D L E R,这样就保证异常得到处理,防止了系统再调用U n h a n d l e d E x c e p t i o n F i l t e r函数。

        在你的异常处理程序中,你可以显示一个对话框,在上面显示一些有关异常的诊断信息。

    用户可以记录下这些信息,并通报给你公司的客户服务部门,以便能够找到程序的问题根源。

    你应该建立这个对话框,这样用户只能结束程序而不能调用调试程序。

        这种方法的缺点是它只能捕捉进程的主线程中发生的异常。如果其他线程在运行,并且其中有一个线程发生了一个未处理异常,系统就要调用内部的 U n h a n d l e d E x c e p t i o n F i l t e r函数。为了改正这一点,需要在所有的辅助线程进入点函数中包含t r y - e x c e p t块。

    25.2.3 包装所有的线程函数

        Wi n d o w s还提供另外一个函数,S e t U n h a n d l e d E x c e p t i o n F i l t e r,利用它可以按S E H格式包装所有的线程函数:

    PTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter(

    PTOP_LEVEL_EXCEPTION_FILTER pTopLevelExceptionFilter);

        在进程调用这些函数之后,进程的任何线程中若发生一个未处理的异常,就会导致调用程序自己的异常过滤器。需要将这个过滤器的地址作为参数传递给 S e t U n h a n d l e d E x c e p t i o n F i l t e r。过滤器函数原型必须是下面的样子:

    LONG UnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo);

        你可能会注意到这个函数同U n h a n d l e d E x c e p t i o n F i l t e r函数的形式是一样的。程序员可以在自己的异常过滤器中执行任何想做的处理,但要返回三个 E X C E P T I O N _ *标识符中的一个。表2 5 - 1给出了当返回各标识符时所发生的事。


        为了使 U n h a n d l e d E x c e p t i o n F i l t e r函数再成为默认的过滤器,可以调用 S e t U n h a n d l e dE x c e p t i o n F i l t e r并传递 N U L L给它。而且,每当设置一个新的未处理的异常过滤器时,SetUnhandled ExceptionFilter就返回以前安装的异常过滤器的地址。如果 Unhandled ExceptionF i l t e r是当前所安装的过滤器,则这个返回的地址就是 N U L L。    如果你自己的过滤器要返回E X C E P T I O N _ C O N T I N U E _ S E A R C H,你就应该调用以前安装的过滤器,其地址通过S e t U n h a n d l e d E x c e p t i o n F i l t e r函数返回。

    以下是一个测试例子:


    25.2.4 自动调用调试程序

        现在再介绍关闭U n h a n d l e d E x c e p t i o n F i l t e r消息框的最后一种方法。在前面提到的同一个注册表子关键字里,还有另外一个数据值,名为 A u t o。这个值用来规定U n h a n d l e d E x c e p t i o n F i l t e r是应该显示消息框,还是仅启动调试程序。如果 A u t o设置成1U n h a n d l e d E x c e p t i o n F i l t e r就不显示消息框向用户报告异常,而是立即调用调试程序。如果 A u t o子关键设置成 0U n h a n d l e d E x c e p t i o n F i l t e r就显示异常消息框,并按前面描述的那样操作。



    25.3 程序员自己调用U n h a n d l e d E x c e p t i o n F i l t e r

        U n h a n d l e d E x c e p t i o n F i l t e r函数是一个公开的、文档完备的 Wi n d o w s函数,程序员可以直接在自己的代码中调用这个函数。这里是使用这个函数的一个例子:

     

        在F u n c a d e l i c函数中,t r y块中的一个异常导致E x p F l t r函数被调用。G e t E x c e p t i o n I n f o r m a t i o n的返回值作为参数传递给 E x p F l t r函数。在异常过滤器内,要确定异常代码并与 E X C E P T I O N _A C C E S S _ V I O L AT I O N相比较。如果发生一个存取违规,异常过滤器改正这个问题,并从过滤器返回E X C E P T I O N _ C O N T I N U E _ E X E C U T I O N。这个返回值导致系统从最初引起异常的指令继续执行。

    如果发生了其他异常,E x p F l t r调用U n h a n d l e d E x c e p t i o n F i l t e r,将E X C E P T I O N _ P O I N T E R S结构的地址传递给它作为参数。U n h a n d l e d E x c e p t i o n F i l t e r显示消息框,可使程序员结束进程或开始调试进程。U n h a n d l e d E x c e p t i o n F i l t e r的返回值再由E x p F l t r返回。

     

     

     

     

     

  • 相关阅读:
    ant 软件包不存在报错
    在 Internet Explorer 中使用 Windows 窗体控件
    智能客户端
    Back to the Future with Smart Clients
    "Automation 服务器不能创建对象" 的解决方案
    Top 10 Reasons for Developers to Create Smart Clients
    Updater Application Block for .NET
    Smart Client Application Model and the .NET Framework 1.1
    Security and Versioning Models in the Windows Forms Engine Help You Create and Deploy Smart Clients
    智能客户端技术总结(二)
  • 原文地址:https://www.cnblogs.com/csnd/p/12062092.html
Copyright © 2011-2022 走看看