BadImageFormatException
being thrown when compiled with Visual Studio 2010 (VS2010), regardless of .NET version (2.0, 3.0, 3.5 or 4), platform or configuration. The problem is not reproducible in Visual Studio 2012 (VS2012) and above.How can this be avoided?
When stepping into the in
in Main
in the code in the MVCE below (which would normally move the execution to the iterator method), a BadImageFormatException
is thrown when the code is compiled in Visual Studio 2010:
but not in Visual Studio 2012 and above:
public class Program
{
public static void Main(string[] args)
{
foreach ( var item in new ScrappyDoo().GetIEnumerableItems() )
Console.WriteLine(item.ToString());
}
}
public class ScoobyDoo<T>
where T : new()
{
public virtual IEnumerable<T> GetIEnumerableItems()
{
yield return new T();
}
}
public class ScrappyDoo : ScoobyDoo<object>
{
public override IEnumerable<object> GetIEnumerableItems()
{
foreach ( var item in base.GetIEnumerableItems() )
yield return item;
}
}
When inspecting the code with ILSpy, the compiled IL for ScrappyDoo.GetIEnumerableItems
was the same for both the VS2010 and VS2012 binaries:
.method public hidebysig virtual
instance class [mscorlib]System.Collections.Generic.IEnumerable`1<object> GetIEnumerableItems () cil managed
{
// Method begins at RVA 0x244c
// Code size 21 (0x15)
.maxstack 2
.locals init (
[0] class MysteryMachine.ScrappyDoo/'<GetIEnumerableItems>d__0',
[1] class [mscorlib]System.Collections.Generic.IEnumerable`1<object>
)
IL_0000: ldc.i4.s -2
IL_0002: newobj instance void MysteryMachine.ScrappyDoo/'<GetIEnumerableItems>d__0'::.ctor(int32)
IL_0007: stloc.0
IL_0008: ldloc.0
IL_0009: ldarg.0
IL_000a: stfld class MysteryMachine.ScrappyDoo MysteryMachine.ScrappyDoo/'<GetIEnumerableItems>d__0'::'<>4__this'
IL_000f: ldloc.0
IL_0010: stloc.1
IL_0011: br.s IL_0013
IL_0013: ldloc.1
IL_0014: ret
} // end of method ScrappyDoo::GetIEnumerableItems
Likewise, the IL for the Main
method is the same for both VS2010 and VS2012 binaries:
.method public hidebysig static
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x2050
// Code size 69 (0x45)
.maxstack 2
.entrypoint
.locals init (
[0] object item,
[1] class [mscorlib]System.Collections.Generic.IEnumerator`1<object> CS$5$0000,
[2] bool CS$4$0001
)
IL_0000: nop
IL_0001: nop
IL_0002: newobj instance void MysteryMachine.ScrappyDoo::.ctor()
IL_0007: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerable`1<!0> class MysteryMachine.ScoobyDoo`1<object>::get_GetIEnumerableItems()
IL_000c: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<object>::GetEnumerator()
IL_0011: stloc.1
.try
{
IL_0012: br.s IL_0027
// loop start (head: IL_0027)
IL_0014: ldloc.1
IL_0015: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<object>::get_Current()
IL_001a: stloc.0
IL_001b: ldloc.0
IL_001c: callvirt instance string [mscorlib]System.Object::ToString()
IL_0021: call void [mscorlib]System.Console::WriteLine(string)
IL_0026: nop
IL_0027: ldloc.1
IL_0028: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_002d: stloc.2
IL_002e: ldloc.2
IL_002f: brtrue.s IL_0014
// end loop
IL_0031: leave.s IL_0043
} // end .try
finally
{
IL_0033: ldloc.1
IL_0034: ldnull
IL_0035: ceq
IL_0037: stloc.2
IL_0038: ldloc.2
IL_0039: brtrue.s IL_0042
IL_003b: ldloc.1
IL_003c: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0041: nop
IL_0042: endfinally
} // end handler
IL_0043: nop
IL_0044: ret
} // end of method Program::Main
In the binaries compiled by VS2012, there is a method, <>n__FabricatedMethod4
, which doesn't appear in VS2010:
VS2012:
VS2010:
ILSpy is unable to inspect the IL for the 'broken' method in the VS2010 binaries, and encounters the following exception:
System.NullReferenceException: Object reference not set to an instance of an object.
at ICSharpCode.Decompiler.Disassembler.DisassemblerHelpers.WriteTo(TypeReference type, ITextOutput writer, ILNameSyntax syntax)
at ICSharpCode.Decompiler.Disassembler.DisassemblerHelpers.WriteTo(TypeReference type, ITextOutput writer, ILNameSyntax syntax)
at ICSharpCode.Decompiler.Disassembler.ReflectionDisassembler.DisassembleMethodInternal(MethodDefinition method)
at ICSharpCode.ILSpy.TextView.DecompilerTextView.DecompileNodes(DecompilationContext context, ITextOutput textOutput)
at ICSharpCode.ILSpy.TextView.DecompilerTextView.<>c__DisplayClass31_0.<DecompileAsync>b__0()
Likewise, it is unable to view the contents of the ScrappyDoo.GetIEnumerableItems
method as C# and shows a similar exception:
ICSharpCode.Decompiler.DecompilerException: Error decompiling System.Collections.Generic.IEnumerable`1<System.Object> MysteryMachine.ScrappyDoo::GetIEnumerableItems()
---> System.NullReferenceException: Object reference not set to an instance of an object.
// stack trace elided
When inspecting the binaries with DotPeek, the decompiled code for the VS2010- and VS2012-compiled code differs in the expression of the foreach
statement:
VS2010:
// ISSUE: reference to a compiler-generated method
foreach (object obj in (IEnumerable<object>) this.<>n__FabricatedMethod4())
yield return obj;
VS2012 (note that the decompiled C# is the same as the source, as expected):
foreach (object obj in base.GetIEnumerableItems())
yield return obj;
The problem is not resolved by changing the method to a property, or by adding more logic into either the base or the override.
Changing the base method to return IEnumerable<object>
instead of IEnumerable<T>
fixes the problem (in this contrived case), but this is not an acceptable solution.
The problem occurs when targeting .NET 2.0, .NET 3.0, .NET 3.5, and .NET 4 in VS2010. When compiled with VS2012 and above, the target framework version is irrelevant and the code behaves as expected.
I'm aware that Visual Studio doesn't compile code - it just invokes MSBuild (or Roslyn), but this problem is still an issue on a machine with VS2010 and VS2012 installed: when running the code in VS2010 the problem persists, and when running in VS2012 it doesn't. Upon setting the build output verbosity to Diagnostic, I found that both VS2010 and VS2012 are using the same MSBuild binaries at
C:\Windows\Microsoft.NET\Framework\v4.0.30319
The problem does not appear in VS2015 (using Roslyn to compile) - the IL is different, but I guess that's to be expected.
I need to use Visual Studio 2010 as, where I work, we do some development on Windows XP which only supports 2010 and below.
PEVerify gives the following output for the VS2010-compiled code:
> peverify MysteryMachine2010.exe
Microsoft (R) .NET Framework PE Verifier. Version 4.0.30319.0
Copyright (c) Microsoft Corporation. All rights reserved.
[IL]: Error: [MysteryMachine2010.exe : MysteryMachine.ScrappyDoo::<>n__FabricatedMethod4] [HRESULT 0x8007000B] - An attempt was made to load a program with an incorrect format.
[IL]: Error: [MysteryMachine2010.exe : MysteryMachine.ScrappyDoo+<getIEnumerableItems>d__0::MoveNext] [HRESULT 0x8007000B] - An attempt was made to load a program with an incorrect format.
2 Error(s) Verifying MysteryMachine2010.exe
whereas for binaries compiled through VS2012 and above, the result is, as expected:
> peverify "MysteryMachine2012.exe"
Microsoft (R) .NET Framework PE Verifier. Version 4.0.30319.0
Copyright (c) Microsoft Corporation. All rights reserved.
All Classes and Methods in MysteryMachine2012.exe Verified.
When running the VS2010-compiled code from the command prompt results in the following output:
> MysteryMachine2010.exe
Unhandled Exception: System.BadImageFormatException: An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)
at MysteryMachine.ScrappyDoo.<getIEnumerableItems>d__0.MoveNext()
at MysteryMachine.Program.Main(String[] args) in MysteryMachine\Program.cs:line 11
Does anybody know why this is, and how it can be avoided? For my actual use case, the iterator in the base has no items in it so I made the base method abstract
and made all the derived classes override it, but that could change at any point, rendering the hack fix useless.
Three suggestions for working around this that don't require abandoning iterators altogether, all relying on getting VS to see past the "discrepancy" of the base and derived return types, which seems to be the source of trouble.
public override IEnumerable<object> GetIEnumerableItems()
{
return getIEnumerableItems();
}
IEnumerable<object> getIEnumerableItems()
{
foreach ( var item in base.GetIEnumerableItems() )
yield return item;
}
public override IEnumerable<object> GetIEnumerableItems()
{
foreach ( var item in baseItems() )
{
yield return item;
}
}
IEnumerable<object> baseItems()
{
return base.GetIEnumerableItems();
}
This could potentially be thwarted by inlining, but I don't think the compiler will bother (traditionally such things are left to the IL level).
public override IEnumerable<object> GetIEnumerableItems()
{
return getIEnumerableItems(base.GetIEnumerableItems());
}
IEnumerable<object> getIEnumerableItems(IEnumerable<object> baseItems)
{
foreach ( var item in baseItems )
yield return item;
}
Disclaimer: none of this is tested, for lack of a VS 2010 installation.