再谈System.BadImageFormatException

今天,当我们继续学习.NET异常处理系列时,我们将查看System.BadImageFormatException。System.BadImageFormatException与GIF或JPG无关,而是在.NET应用程序尝试加载与当前公共语言运行库(CLR)所需的正确格式不匹配的动态链接库(.dll)或可执行文件(.exe)时发生。
在本文中,我们将看到System.BadImageFormatException在.NET异常层次结构中的确切位置,并查看System.BadImageFormatException的一些潜在原因,让我们开始讨论它!

如前所述,System.BadImageFormatException发生在非常特殊的情况下:当.NET试图使用.dll或.exe时,即以某种方式与当前公共语言运行库不兼容。“不兼容的公共语言运行时”的定义可能有所不同,但通常这意味着.NET版本(1.1、2.0等)或各种编译程序集的CPU类型(32位与64位)不匹配。
最后,System.BadImageFormatExceptions表示版本控制不兼容。对于许多现代软件应用程序,的主要版本通常包括打破兼容性问题,防止与以前版本的某些方面向后兼容。.NET程序集(.dll或.exe)基本相同,尝试使用包含不兼容项的两种不同类型的程序集通常会生成System.BadImageFormatException。
为了说明这一点,我们将通过几个不同的例子。我已经包含了下面的完整代码示例以供参考,之后我们将更详细地探讨细节:

using System;
using System.Reflection;
using Utility;

namespace Airbrake.BadImageFormatException
{
    class Program
    {
        static void Main(string[] args)
        {
            LoadingNonDotNetLibraryExample();
            Logging.Log("-----------------");
            DifferingCPUExample();
            Logging.Log("-----------------");
            OldDotNetExample();
        }

        private static void LoadingNonDotNetLibraryExample()
        {
            try
            {
                // Generate path to notepad.exe.
                string filePath = Environment.ExpandEnvironmentVariables("%windir%") + @"\System32\notepad.exe";
                Assembly assem = Assembly.LoadFile(filePath);
            }
            catch (System.BadImageFormatException exception)
            {
                Logging.Log(exception);
            }
        }

        private static void DifferingCPUExample()
        {
            try
            {
                // Load Utility.dll, a 64-bit assembly.
                Assembly assem = Assembly.LoadFrom(@".\Utility.dll");
                Logging.Log(assem.ToString());
            }
            catch (System.BadImageFormatException exception)
            {
                Logging.Log(exception);
            }
        }

        private static void OldDotNetExample()
        {
            try
            {
                // Load Author-1.1.dll (compiled in .NET 1.1).
                Assembly assem = Assembly.LoadFrom(@".\Author-1.1.dll");
                Logging.Log(assem.ToString());
            }
            catch (System.BadImageFormatException exception)
            {
                Logging.Log(exception);
            }
        }
    }
}

using System;
using System.Diagnostics;

namespace Utility
{
    /// <summary>
    /// Houses all logging methods for various debug outputs.
    /// </summary>
    public static class Logging
    {
        /// <summary>
        /// Outputs to <see cref="System.Diagnostics.Debug.WriteLine"/> if DEBUG mode is enabled,
        /// otherwise uses standard <see cref="Console.WriteLine"/>.
        /// </summary>
        /// <param name="value">Value to be output to log.</param>
        public static void Log(object value)
        {
            #if DEBUG
                Debug.WriteLine(value);
            #else
                Console.WriteLine(value);
            #endif
        }

        /// <summary>
        /// When <see cref="Exception"/> parameter is passed, modifies the output to indicate
        /// if <see cref="Exception"/> was expected, based on passed in `expected` parameter.
        /// <para>Outputs the full <see cref="Exception"/> type and message.</para>
        /// </summary>
        /// <param name="exception">The <see cref="Exception"/> to output.</param>
        /// <param name="expected">Boolean indicating if <see cref="Exception"/> was expected.</param>
        public static void Log(Exception exception, bool expected = true)
        {
            string value = $"[{(expected ? "EXPECTED" : "UNEXPECTED")}] {exception.ToString()}: {exception.Message}";
            #if DEBUG
                Debug.WriteLine(value);
            #else
                Console.WriteLine(value);
            #endif
        }
    }
}

引发System.BadImageFormatException的第一种(也可以说是最常见的)方法是尝试使用非托管程序集时,就像它是使用.NET框架创建的程序集一样。非托管程序集是由.NET的公共语言运行库未处理和编译的代码生成的程序集。这包括许多较旧的应用程序和程序集,特别是为32位系统创建的应用程序和程序集。
作为一个例子,这里我们试图加载一个非托管程序集—特别是位于Windows/System32目录中的众所周知的notepad.exe程序集:

private static void LoadingNonDotNetLibraryExample()
{
    try
    {
        // Generate path to notepad.exe.
        string filePath = Environment.ExpandEnvironmentVariables("%windir%") + @"\System32\notepad.exe";
        Assembly assem = Assembly.LoadFile(filePath);
    }
    catch (System.BadImageFormatException exception)
    {
        Logging.Log(exception);
    }
}

.NET对此不满意,因为notepad.exe不受管理(不是使用.NET编译的),因此引发System.BadImageFormatException:

[EXPECTED] System.BadImageFormatException: The module was expected to contain an assembly manifest. (Exception from HRESULT: 0x80131018)
   at System.Reflection.RuntimeAssembly.nLoadFile(String path, Evidence evidence)
   at System.Reflection.Assembly.LoadFile(String path)
   at Airbrake.BadImageFormatException.Program.LoadingNonDotNetLibraryExample() in D:\work\Airbrake.io\Exceptions\.NET\Airbrake.BadImageFormatException\Program.cs:line 26: The module was expected to contain an assembly manifest. (Exception from HRESULT: 0x80131018)

另一种可能引发System.BadImageFormatException的方法是尝试加载使用不同于当前在.NET上执行的CPU类型编译的程序集。
例如,在我们的许多代码片段中,我们一直在使用包含日志类的简单实用程序名称空间,这使得在调试和测试期间输出日志信息更加容易。默认情况下,Utility.dll编译为64位程序集。但是,如果我们将当前的CPU配置切换为以x86(32位)CPU执行,则会遇到一些问题:

private static void DifferingCPUExample()
{
    try
    {
        // Generate path to Utility.dll, a 64-bit assembly.
        Assembly assem = Assembly.LoadFrom(@".\Utility.dll");
        Logging.Log(assem.ToString());
    }
    catch (System.BadImageFormatException exception)
    {
        Logging.Log(exception);
    }
}

当试图加载Utility.dll程序集(64位)时,当将当前代码编译为32位时,.NET抛出System.BadImageFormatException,通知我们这些格式不匹配:

[EXPECTED] System.BadImageFormatException: An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)
   at System.Reflection.RuntimeAssembly.nLoadFile(String path, Evidence evidence)
   at System.Reflection.Assembly.LoadFile(String path)
   at Airbrake.BadImageFormatException.Program.DifferingCPUExample() in D:\work\Airbrake.io\Exceptions\.NET\Airbrake.BadImageFormatException\Program.cs:line 40: An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)

最后,如果尝试加载使用更旧版本的.NET(如.NET 1.1)编译的程序集,我们也会遇到问题:

private static void OldDotNetExample()
{
    try
    {
        // Load Author-1.1.dll (compiled in .NET 1.1).
        Assembly assem = Assembly.LoadFrom(@".\Author-1.1.dll");
        Logging.Log(assem.ToString());
    }
    catch (System.BadImageFormatException exception)
    {
        Logging.Log(exception);
    }
}

在上述情况下,System.BadImageFormatException通常会在编译时抛出,而不是在运行时抛出,因为.NET编译器在试图首先执行任何代码之前都会识别出不兼容。

posted on 2019-12-07 10:31  活着的虫子  阅读(6674)  评论(0编辑  收藏  举报

导航