【问题标题】:How does the CLR recognise a "Corrupt State Exception"?CLR 如何识别“损坏状态异常”?
【发布时间】:2015-01-15 16:58:04
【问题描述】:

背景

我们的应用程序调用一个混合模式程序集,该程序集在其非托管代码中有一个已知错误,会导致抛出 AccessViolationException。

我们的目标是在终止应用程序之前捕获 AV 异常,将其包装,然后抛出一个新异常,该异常将在堆栈的更高位置被捕获并记录下来。

问题

我们的代码没有像我们预期的那样运行......我们用来包装 AV 异常的“普通”异常表现得就像是 CSE,并且没有被我们的顶级异常处理程序捕获.相反,它冒泡到操作系统,终止了该过程。这只会在 Windows 事件日志中留下一条令人困惑的日志消息,表明 InvalidOperationException 以某种方式设法绕过了我们所有的异常处理程序并终止了进程。

请注意,只有将 AV 异常设置为包装异常的内部异常时,才会显示此行为。

这是一个例子:

    [Test]
    public void RunTest()
    {
        try
        {
            ProvokeAccessViolation();
        }
        catch (InvalidOperationException e)
        {
            // We never get here!
            Console.WriteLine("Log exception: " + e);
        }
    }

    [HandleProcessCorruptedStateExceptions]
    [SecurityCritical]
    public static void ProvokeAccessViolation()
    {
        try
        {
            var ptr = new IntPtr(1);
            const bool someValue = true;
            Marshal.StructureToPtr(someValue, ptr, true); // Will throw AccessViolationException
        }
        catch (AccessViolationException ex)
        {
            Console.WriteLine("Caught AV exception: " + ex);
            throw new InvalidOperationException("Wrapping AV exception", ex);
        }
    }

我的问题是,CLR 是如何知道将包装异常(上例中的 InvalidOperationException)视为 CSE 的?

同样,如果您只是从托管代码中抛出一个新的 AccessViolationException,为什么它表现得像 CSE? (...我假设在这两种情况下使用相同的机制)

更新: 继 Hans 的回答之后,这里是一个显示 AV 异常被抛出和捕获的屏幕截图:

【问题讨论】:

    标签: exception-handling clr


    【解决方案1】:
        catch (AccessViolationException ex)
    

    您犯了一个简单的错误,否则可能说明了捕获 CSE 的巨大危险。问题是您还必须知道要捕获哪个特定的 CSE。不止一个。

    是的,这个事故开始是作为操作系统中的 AVE。但并没有保持这种状态,CLR 对由访问较低 64KB 内存中触发的 AVE 有一个特殊规则。它将它们变成NullReferenceException。你没有抓住。修复:

        catch (NullReferenceException ex)
    

    现在可以正常使用了。

    【讨论】:

    • 谢谢 Hans - 有趣的信息...但是我的机器上没有相同的行为(例如,它绝对是调用 Marshal.StructureToPtr() 时抛出的 AVE)。在原始示例中,我对为什么它没有在 RunTest() 中捕获 InvalidOperationException 感兴趣。 CLR 不会将它呈现给 catch 块,因为它认为它是一个 CSE。如果您将 HPCSE 属性添加到 RunTest,它将按照设计吞下 InvalidOperationException。
    • 不,它肯定是被抛出的 NullReferenceException。只需将其更改为 var ptr = new IntPtr(65536); 即可获得 AccessViolationException。
    • 不在我的机器上 :0) ...我附上了一个截图来演示。无论如何,ProvokeAccessViolation() 中的捕获可能只是 catch(Exception ex)。我的问题是为什么你不能在 RunTest() 中捕获 InvalidOperationException?
    • 我无法重现,但没有使用 MSTest 运行器,而是使用了控制台模式应用程序。请确保在 Debug + Exceptions 对话框中关闭 Throw 复选框。
    猜你喜欢
    • 2023-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-26
    • 2017-02-18
    • 2012-02-16
    • 2019-08-31
    • 1970-01-01
    相关资源
    最近更新 更多