今天,當我們繼續學習.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編譯器在試圖首先執行任何代碼之前都會識別出不兼容。