2023年1月

引发绕过所有异常处理程序(基于帧或矢量)的异常。引发此异常将终止应用程序并调用Windows错误报告(如果Windows错误报告正在运行)。

原型:

VOID WINAPI RaiseFailFastException(
  __in_opt  PEXCEPTION_RECORD pExceptionRecord,
  __in_opt  PCONTEXT pContextRecord,
  __in      DWORD dwFlags
);

默认纯虚拟函数调用错误处理程序。 当调用纯虚拟成员函数时,编译器生成调用此函数的代码。

原型:

extern "C" int __cdecl _purecall();

在本文中,我们将不解释为什么会提示“纯虚拟函数调用”和如何提示“纯虚拟函数调用”,而是详细解释在win32平台的构造函数/析构函数中直接/间接调用纯虚拟函数时程序本身。在开始时,将显示一个经典示例,在这个示例中,它将提示一个带有“纯虚拟函数调用”的消息框。

    /** 
     * "pure virtual function call" on win32 platform 
     * filename: testWin32PVFC.cpp 
     */  
    #include <iostream>  
     
    #define PI 3.1415926  
    using namespace std;  
      
    class Shape  
    {  
    private:  
        double ValuePerSquareUnit;  
      
    protected:  
        Shape(double valuePerSquareUnit):  
            ValuePerSquareUnit(valuePerSquareUnit)  
        {  
            //error LNK2001: unresolved external symbol "public: virtual double __thiscall Shape::area(void)const " (?area@Shape@@UBENXZ)  
            //std::cout << "creating shape, area = " << area() << std::endl;  
            std::cout << "creating shape, value = " << value() << std::endl;  //indirectly call pure virtual function in constructor  
        }  
      
    public:  
        virtual double area() const = 0;  
      
        double value() const  
        {  
            return ValuePerSquareUnit * area();  
        }  
      
        virtual ~Shape()  
        {  
            printf("Shape::~Shape() is called");  
        }  
      
        double getPerSquareUnit()  
        {  
            return ValuePerSquareUnit;  
        }  
    };  
      
    class Rectangle : public Shape  
    {  
    private:  
        double Width;  
        double Height;  
      
    public:  
        Rectangle(double width, double height, double valuePerSquareUnit):  
            Shape(valuePerSquareUnit),Width(width),Height(height)  
        {  
        }  
      
        virtual ~Rectangle()  //can be removed  
        {  
        }  
      
        virtual double area() const  
        {  
            return Width * Height;  
        }  
      
    };  
      
    class Circle: public Shape  
    {  
        double Radius;  
      
    public:  
        Circle(double radius, double valuePerSquareUnit):  
            Shape(valuePerSquareUnit),Radius(radius)  
        {  
        }  
      
        virtual ~Circle()  //can be removed  
        {  
        }  
      
        virtual double area() const  
        {  
            return PI * Radius * Radius;  
        }  
    };  
      
      
    int main()  
    {  
        Rectangle* pr = new Rectangle(30, 20, 10);  
        Circle* pc = new Circle(15, 10);  
      
        //invoke Rectangle::area()  
        printf("rectangle: area = %.2f, PerSquareUnit = %.2f, value = %.2f/n", pr->area(), pr->getPerSquareUnit(), pr->value());  
        //invoke Circle::area()  
        printf("circle   : area = %.2f, PerSquareUnit = %.2f, value = %.2f/n", pc->area(), pc->getPerSquareUnit(), pc->value());  
      
        Shape* shape;  
        shape = pr;  
        printf("rectangle: area = %.2f, PerSquareUnit = %.2f, value = %.2f/n", shape->area(), shape->getPerSquareUnit(), shape->value());  
      
        shape = pc;  
        printf("circle   : area = %.2f, PerSquareUnit = %.2f, value = %.2f/n", shape->area(), shape->getPerSquareUnit(), shape->value());  
      
        return 0;  
    }  


当我们提到CLR里的“异常”,要注意一个很重要的区别。有通过如C#的try/catch/finally暴露给应用程序,并由运行时提供机制全权实现的托管异常。也有运行时自己使用的异常。大部分运行时开发人员很少需要想到如何实现并暴露托管异常模型。但每个运行时开发人员都应该懂得CLR实现里是怎么使用异常的。为了保持区分,本文将托管程序抛出并捕捉的称为托管异常,而将运行时自己使用的错误处理方式称为 CLR内部异常。本文主要讨论CLR内部异常。

异常在什么地方有用?

异常几乎在所有地方都有用。最有用的地方就是抛出或捕捉异常的函数里,因为需要显式编写代码来抛出异常或者捕捉其并优雅的处理异常。即使一个函数本身不抛出异常,它也有可能调用抛出异常的函数。这样该函数必须在异常抛出的时候行为正常。明智的使用支持物(holders)可以极大简化正确编写这类代码。

为什么CLR内部异常是不同的?

CLR内部异常更像C++异常,但不完全是。CLR可以在Mac OSX、BSD还有Windows下编译。操作系统和编译器的差异使得我们不能仅使用标准C++的try/catch。另外,CLR内部异常还提供了类似托管代码的“finally”和“fault”这样的功能。

通过一些宏,编写异常处理代码就像标准C++那样简单。

捕捉异常

EX_TRY

最基本的宏是:EX_TRY / EX_CATCH / EX_END_CATCH,使用方法如下:

EX_TRY//调用一些函数,也许会抛出一个异常
Bar();
EX_CATCH
//在这里,那就有错误发生了 m_finalDisposition =terminallyHopeless;
EX_END_CATCH(RethrowTransientExceptions)


不捕捉某一个异常

常常有这种情况,代码不需要捕捉异常,但需要执行一些清理或者修正操作。虽然不总是,支持物(holders)经常用在这种场景里。在支持物(holders)不适用的情况里,CLR提供了两个“finally”块的变种。

EX_TRY_FOR_FINALLY

当需要在代码退出时执行修正操作时,一个finally块就比较合适。在CLR里有一系列的宏来实现try/finally:

EX_TRY_FOR_FINALLY//code
EX_FINALLY//exit and/or backout code
EX_END_FINALLY