每当在Visual C++程序中抛出异常时,如何运行一些代码?

5

如果C++程序中抛出异常,控制权将要么转移到异常处理程序,要么调用terminate()

即使程序在处理程序中(或者terminate()处理程序中)发出一些诊断信息,这可能都已经太晚了 - 最有价值的是在抛出异常的地方的调用堆栈中,而不是在处理程序中。

在Windows上,可以使用[StackWalk64()]1函数获得调用堆栈。关键在于如何在正确的时刻调用该函数。

是否有一种方法可以让Visual C++程序每次抛出异常(或未设置处理程序的异常)时执行一些用户代码?

6个回答

3
如果你想在发生SEH异常时执行某些操作,例如访问冲突时,你可以简单地捕获SEH异常(使用__finally或将其转换为C++异常(请参见这里)),并访问异常中的上下文,即异常抛出时的上下文。然后,您可以使用StackWalker生成调用堆栈或迷你转储。我认为制作迷你转储更好。
如果你想在C++异常被抛出时捕获它们,并且你没有访问C++异常类源代码的权限,那么你需要变得聪明一点。我通过在自定义调试器下运行目标进程来解决这个问题 - 使用Debug API(请参见这里),该API会在抛出异常时获得通知。此时,您可以创建目标进程的迷你转储或调用堆栈。

2

1

0
有没有办法让 Visual C++ 程序在每次抛出异常(或未设置处理程序的异常)时执行一些用户代码?
将该代码放入您的异常基类构造函数中即可。

好主意,但如果抛出一些无关的异常,这并不会有所帮助。 - sharptooth
@sharptooth:你可以执行异常转换。对我来说,将库异常(例如Boost)包装在自己的异常类型中始终是一个好主意。 - Daniel Lidström
@Daniel Lidström:是的,异常翻译很好,但了解原始异常来自哪里通常也很有用。 - sharptooth

0

这篇文章是关于如何在Visual C++中捕获所有不同类型的异常的好文章。
它还为您提供了一个崩溃转储,可用于调试。


0

当语言不支持它,而你又离不开它时,就得动手“hack”了… :-/

#include <iostream>
#include <stdexcept>

namespace Throw_From
{
    struct Line
    {
        Line& set(int x) { x_ = x; return *this; }
        int x_;

        template <typename T>
        void operator=(const T& t) const
        {                                                                       
            throw t;
        }                                                                       
    };                                                                          
    Line line;                                                                  
}                                                                               

#define throw Throw_From::line.set(__LINE__) =                                  

void fn2()                                                                      
{                                                                               
    throw std::runtime_error("abc");                                            
}                                                                               

void fn1()                                                                      
{                                                                               
    fn2();                                                                      
}                                                                               

int main()                                                                      
{                                                                               
    try
    {
        fn1();
    }
    catch (const std::runtime_error& x)
    {
        std::cout << Throw_From::line.x_ << '\n';
    }
}

3
实际上,这里还有一个更大的问题,如果你包含任何标准库头文件,就不允许定义与关键字在词法上完全相同的宏,因此 #define throw whatever 是不可行的。 - James McNellis
1
@James:通常情况下,当人们开始考虑在异常情况下调用特定于平台的堆栈转储时,他们并不关心可移植性或标准,并会检查他们的头文件以查看是否有效。这是权宜之计。 (嗯...不能重载运算符,太晚了) - Tony Delroy
1
#define THROW(E) (do_stuff(), (throw E)) - Fred Nurk
1
@Tony:那些人通过未检测到的UB使糟糕的情况变得更糟。 - Fred Nurk
1
@Tony,你的解决方案在你有代码的情况下很有用,但如果异常来自第三方库,它就无法帮助。而且它使用全局静态对象Throw_From::line来存储额外信息,这是不线程安全的。 - Dialecticus
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接