使用IEnumerable和多重继承时出现BadImageFormatException错误

4
今天我遇到了一个非常奇怪的问题,虽然我解决了它,但我仍然不明白为什么会发生这种情况。下面是问题的场景:

编辑

我改变了场景让它更加简单:我有一个执行代码的程序和两个导入器,一个带有通用类型的基类和另一个类(ImplementingImporter),它只是调用基础方法并对其进行迭代。 这是完整的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace IEnumeratorLoadProblem {
    class Program {
        static void Main(string[] args) {

            var importer = new ImplementingImporter();
            try {
                var data = importer.GetData().ToArray();
            } catch (BadImageFormatException ex) {                
                Console.WriteLine("Why does this fail? " + ex.ToString());
            }

            Console.WriteLine("Press enter to quit");
            Console.ReadLine();
        }
    }

    class BaseClassImporter<T> {

        public virtual IEnumerable<T> GetData() {
            yield break;
        }
    }

    class ImplementingImporter : BaseClassImporter<int> {
        public override IEnumerable<int> GetData() {
            // iterating seems to cause the problem
            foreach(var dataByBaseImpl in base.GetData()) {
                yield return dataByBaseImpl;
            }
        }
    }
}

我遇到了以下错误:

System.BadImageFormatException: 尝试加载格式不正确的程序时发生错误。(HRESULT 异常:0x8007000B)

当我将代码从使用的导入器更改为此后,它就能够运行
class ImplementingImporter : BaseClassImporter<int> {
    protected override IEnumerable<int> GetData() {
        return base.GetData();
    }
}

很遗憾,我无法查看生成的IL代码,因为ILSpy和Reflector.NET(版本6)都显示了内部错误(我认为这是一个ArgumentOutOfRangeException)。我害怕使用ildasm,因此没有尝试直接查看IL代码。
我猜想这与生成的IL代码有关,但我无法想象造成问题的情况。
有什么想法吗?如果情况不够清楚,请留下评论,我会尝试让它更清晰。
编辑:使用的.NET版本为4.0。应用程序是使用VS 2010 SP1创建的控制台应用程序。构建平台目标是AnyCPu,但使用x86时也出现了问题。我的机器是64位系统(Windows 7)。即使使用.NET 4.0客户端配置文件,异常也会发生。
示例是单个项目,未使用外部/非托管库,因此只应该出现建议的问题(例如在运行64位时引用32位程序集)。

这个异常通常发生在你正在32/64位进程中尝试加载其他“位数”的DLL时。一个进程只能针对一个平台,一旦选择了一个平台,只能加载相同平台的DLL(32位一旦选定,就始终是32位 - 64位也是如此)。确保你正在构建的DLL的目标平台都是相同的,可以明确指定为x86/x64(但都要相同),或者使用AnyCPU隐式选择位数。 - Adam Houldsworth
这也是我的第一个猜测,所以我检查了选项并创建了一个单一的项目来重现问题(当一切都在一个程序集中时,我想它不应该是构建问题)。我将在问题中添加有关使用的.NET版本等其他信息。 - Bernhard Kircher
同样的规则也适用于其他第三方托管和非托管的在进程中运行的DLL。 - Adam Houldsworth
该解决方案仅包含一个项目,不包括外部dll文件。将给定的场景复制到一个文件中并运行应该是可行的。也许我可以简化这个场景。 - Bernhard Kircher
我更新了示例以重现问题。 - Bernhard Kircher
有趣,我猜测这与编译器为支持“yield”关键字而发出的代码有关。 - Adam Houldsworth
2个回答

4

这似乎是使用yield return语句的一个错误:

https://connect.microsoft.com/VisualStudio/feedback/details/677532/an-attempt-was-made-to-load-a-program-with-an-incorrect-format-exception-from-hresult-0x8007000b#details

他们说这在VS2012中已经修复了(或者“VS 2010之后的版本”)。

我正在运行一个针对.NET Framework 4的控制台应用程序,使用VS2010 SP1,并确认我得到与您相同的错误。我没有可用于尝试此操作的VS2012安装。

这里还有一个类似的问题:

迭代器块和继承

另一个可疑的类似例子(这次是异步的,但再次触发了MoveNext):

C#5 AsyncCtp BadImageFormatException

其他资源:


谢谢提供链接。我仍然可以使用connect.microsoft.com上的代码重现错误。我猜微软对“fixed”的解释与我的不同。 - Bernhard Kircher
1
@BernhardKircher 是的,我也可以重现它。 - Adam Houldsworth
@BernhardKircher 发现了另一个奇怪的情况,这次是与异步相关的,但仍然涉及到生成的代码。 - Adam Houldsworth
2
@BernhardKircher:他们说这个问题已经在VS2010之后的构建中得到了解决,所以你可能需要尝试使用VS2012才能真正地获得修复。 - Jason Williams
@JasonWilliams 我其实没有注意到那个问题,我已经修改了我的答案。 - Adam Houldsworth
+1 我已经浪费了几个小时在这上面……终于有人指出真正的原因。 - Konrad Morawski

0

确保所有项目使用相同的编译选项(AnyCPU、x86或x64)。

如果您使用任何外部dll,请确保它们是兼容的(即在x86机器上为32位,在x64机器上为64位)。

如果您想在x64机器上使用32位dll,则需要将主要的.exe设置为编译为“x86”,而不是“Any CPU”-这将强制整个应用程序在64位PC上作为32位进程运行。 (如果您不这样做,您的exe将在x64机器上被JIT编译为64位应用程序,然后尝试调用您的32位dll,这将引发您正在获取的异常)


该解决方案仅包含一个项目,不包括外部dll文件。将给定的场景复制到一个文件中并运行应该是可行的。也许我可以简化这个场景... - Bernhard Kircher

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