为什么这段代码会抛出 System.ExecutionEngineException 异常?

17

背景: 我正在使用DirectX 9.0托管库将3D点的数组转换为2D屏幕坐标。为了提高速度,我使用UnsafeNativeMethods来执行所有的变换。

问题: 如果使用我的自定义线剪辑函数,我的应用程序会在没有抛出任何异常的情况下崩溃。花了一段时间,我才发现它会抛出一个无法捕获System.ExecutionEngineException。我已经将其缩小到发生在剪辑函数的最后两行。

List<Vector3> verticesAfterClipping = new List<Vector3>;
public unsafe void ClipLine(Line lineToClip)
{
    this.verticesAfterClipping.Clear();

    // Clipping algorithm happens here... (this is psuedo-code of what it does)
    foreach(Vertex in lineToClip.Vertices)
    {
        bool thisIsClipped =   // Set to whether this vertex is clipped
        bool lastWasClipped =  // Set to whether last vertex was clipped

        if(thisIsClipped == false && lastWasClipped == true)
        {
            verticesAfterClipping.Add( /* intersection on clipping plane */ );
            verticesAfterClipping.Add( /* thisVertex */ );
        }
        else if (thisIsClipped == false && lastWasClipped == false)
        {
            verticesAfterClipping.Add( /* thisVertex */ );
        }
        else if (thisIsClipped == true && lastWasClipped == false)
        {
            verticesAfterClipping.Add(/* intersection on clipping plane */);
        }
    }

    // THIS IS WHERE BAD THINGS HAPPEN
    lineToClip.Vertices = new Vertex[verticesAfterClipping.Count];
    verticesAfterClipping.CopyTo(lineToClip.Vertices, 0);
}

verticesAfterClipping列表复制到lineToClip顶点后,lineToClip对象随后传递给一个不安全的本地方法,该方法将这些顶点转换为2D顶点。从我能看到的一切,在调试模式下逐步执行它是完全正常的,直到它突然死机。

我就是无法弄清楚出了什么问题。任何帮助将不胜感激。

3个回答

30

问题可能并不是在抛出异常的那行代码中发生的。这可能只是先前发生的某些事情的症状。

System.ExecutionEngineException 异常是当 CLR检测到出现了严重错误时抛出的。这可能发生在问题发生后相当长的一段时间内。这是因为该异常通常是由于内部数据结构损坏而导致的 - CLR发现某些东西进入了一个没有意义的状态。它会抛出无法捕获的异常,因为继续操作不安全。

所以你可能有一些与系统的完全无关的代码在损坏某些东西,但只有在运行这个特定的代码片段时才会变得明显。你展示的代码可能没什么问题。(当然也可能有问题...我没有看到任何明显的错误,但我不太了解DX 9托管库。例如,我看不到这个方法需要使用不安全关键字的哪个功能。)

不幸的是,这意味着你需要开始扩大调查范围。几乎所有使用不安全代码或COM互操作的内容都有可能存在问题。遗憾的是,这将是一个漫长而繁琐的过程。你可以尝试逐步简化程序的方式来处理:哪一部分代码最简单且能够说明问题?(例如,如果你将你展示的代码放入一个除了调用该方法的最简单可能的应用程序之外没有其他内容的应用程序中,它是否仍然失败?)


当你说异常并没有在这两行代码中发生时,你是正确的,但它确实是由这两行代码引起的。我替换了这些代码,并使用另一个临时缓冲区来复制剪切的顶点,现在一切都正常了。我猜测当该行代码传递给不安全的本地 DirectX 方法时,发生了某种内存访问违规。 - tbridge
14
不要假设没有ExecutionEngineException就意味着问题已经解决了。这可能仅仅是因为CLR不再检测到它了。通过替换那些代码行,你现在可能处于数据被破坏但不再抛出异常的情况下。(CLR不能保证每次出现问题时都会抛出ExecutionEngineException。它只有在注意到异常时才会抛出。)所以我会担心——我肯定会试图弄清楚崩溃是如何发生的,因为现在没有理由相信你真正解决了它。 - Ian Griffiths

3

我和其他人一样遇到了不同的库的问题。在我的情况下,所有这些都始于很久以前,因为我必须在64位环境中运行一个32位的.net应用程序。嗯,这给我带来了很多麻烦,架构之间的兼容性或.NET框架的CLR之间的兼容性也可能是你的问题所在。

PS:现在我知道我的问题是什么,但我不知道它在哪里


3
这是我遇到的错误原因。我的项目是AnyCPU,但需要x64。当我看到你的评论后,立刻尝试了一下,结果立即解决了问题。 - J. Rockwood

0
我有两个类来处理数据库中的一个特定表。
StorageManager - 一个包含处理与数据库直接交互的方法的类。
Controller - 一个包含处理API调用的方法的类。
当要创建表中的新条目时,我会检查子表是否已存在某些值。
我将这些检查预先存在/重复的方法写在了错误的类中,结果导致了执行引擎异常被抛出。
将这些方法移动到与正确的数据库上下文相对应的位置似乎解决了这个问题。

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