再談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編譯器在試圖首先執行任何代碼之前都會識別出不兼容。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM