2023年1月

简介

开发人员面临的最大问题之一是生产问题何时发生。开发人员必须快速找到问题的根本原因,或者在开发环境中复制相同的问题来解决问题。由于时间是至关重要的,在这方面,迟迟得不到根本原因可能导致客户的金钱损失或导致公司的声誉损失。有效地解决生产问题和缩短周转时间的方法很少。

调试问题的方法

  • 检查调用堆栈的日志
  • 启用跟踪并检查跟踪输出
  • 在开发环境中复制场景并使用Visual Studio进行调试
  • 使用程序数据库(PDB)文件
  • 远程调试

检查调用堆栈的日志

这是调试问题的最常见方法,即获取用户日志文件并检查其中的异常。如果异常是已知的并用自定义消息处理,那么它是有用的,但是如果异常是未处理的,那么很难找出异常是如何发生的。调用堆栈在这种方法中很有用,但调用堆栈仍然提供方法级信息。方法级信息不显示是哪一行导致了问题。为了获得准确的行信息,我们需要使用程序数据库(PDB)文件和应用程序。PDB将在下面详细介绍。日志的另一个缺点是我们不能在应用程序中进行大量的日志记录,特别是如果日志是在输出文件中写入的。这会大大降低应用程序的性能。因此,应根据需要限制日志记录。

启用跟踪并检查跟踪输出

可以通过配置启用跟踪,并且可以重新运行应用程序以检查跟踪输出。如果trace被禁用,程序将忽略trace语句。因此,可以在代码中使用跟踪开关使用跟踪语句。但是这种方法仍然有局限性,因为它需要为每一个逻辑代码编写,而这些代码的维护有点繁琐。

在开发环境中复制场景并使用Visual Studio进行调试

由于通常的做法是只在客户机上部署程序集(编译的代码)和框架,因此很明显,客户机将没有用于调试的应用程序代码或Visual Studio IDE/Debugger。所以为了找到根本原因,开发人员需要用类似的场景设置开发环境,并在开发人员的机器上调试它。

这是找到问题根源的最可靠的方法,但它有严重的局限性。

  1. 开发环境和生产环境中的数据不能相同,因此与数据相关的问题很难重新创建。
  2. 这是一项耗费时间的任务,而且根据应用程序工作所涉及的多个系统,在开发过程中查看复制问题的关键性可能很难设置。

使用程序数据库(PDB)文件

这是在客户机上调试问题的最简单方法。但是什么是PDB文件?我们通常在编译应用程序时在编译文件夹中看到它们。程序可以在没有它们存在的情况下运行。因此,堆栈跟踪可以更加丰富。当我们将PDB文件与EXE/DLL一起包含,然后复制场景时,当前堆栈跟踪还将提供发生错误的行号。这有助于我们了解错误发生的确切位置,然后对其进行修正或提供修正。让我们举一个例子来说明PDB文件的使用。

PDB文件是特定于DLL/EXE的生成文件。它们包含链接到相应DLL/EXE的GUID。因此,即使代码没有更改,一个生成的PDB文件也不能与另一个生成的DLL/EXE一起使用。因此,一旦创建了一个内置发布模式,就需要将PDB存储在某个存储库(即“Symbol Server”)中。向PDB提供版本构建是可选的。但是提供它并没有太大的威胁,因为您的程序集和EXE已经包含了您的源代码。要反向工程您的可执行文件是可能的,可能类似的努力也可以放在PDB文件。因此,总的来说,除非您的安装包是特定于大小的,否则不会有太多问题,因为pdb文件可能会占用大量空间。

远程调试

PDB文件是开始调试的好方法,但如果您可以连接到客户端的计算机,则可以从计算机调试应用程序。远程调试很容易实现。您只需执行以下操作:

您需要在客户机上设置远程调试工具。这些工具可以在Microsoft网站上找到,例如https://www.Microsoft.com/en-us/download/details.aspx?id=48155。URL可能会更改,因此最好搜索“Visual Studio 20XX的远程工具”。

 

 

这些就是开发人员如何加快调试应用程序的方法。还有其他一些方法,如分析应用程序内存转储并找出崩溃或内存泄漏的原因。

这篇文章解释了如何使用驱动程序验证工具来分析崩溃转储文件。

使用Microsoft驱动程序验证工具

如果您曾经使用Windows的调试工具来分析崩溃转储,那么毫无疑问,您已经使用WinDbg打开了一个崩溃转储文件。WinDbg将对崩溃文件执行内部分析,并建议您从!analyze命令开始。该命令输出堆栈以及许多其他信息。执行此操作时,堆栈底部将显示转换为内核模式的线程,然后从那里开始,您向上遍历堆栈以查看是否存在罪魁祸首驱动程序。虽然这是一种可靠的调试技术,但有时崩溃转储或其中的一组将无法分析。内存中没有任何模式可以指出导致系统崩溃的原因,或者内存可能已损坏,因为崩溃转储文件实际上指向Ntsokrnl.exe或win32k.sys。
有一种方法可以通过使用Microsoft驱动程序验证工具将无法处理的崩溃转换为可分析的崩溃。此工具随Windows的每个版本一起提供,不是单独安装的。它不可见,因此不在“开始”菜单或“控制面板”中的“管理工具”中。通过在“开始”菜单的“运行”框中键入“verifier”来启动它,但是为了使用它,您应该知道它是如何工作的。本文将介绍如何使用此工具将无法分析的崩溃转换为可分析的崩溃。驱动程序验证工具包含许多选项,其中一些应该严格避免。
要启动驱动程序验证程序,请在“开始”菜单的“运行”框中键入“Verifier.exe”。第一个数据框显示列表选项。选择的选项是“创建自定义设置(用于代码开发人员)”。避免使用默认的“标准设置”选项。单击“下一步”后,我们选择“从完整列表中选择各个设置”。注意,我们没有选择任何默认设置。单击Next之后,我们会看到一个选项列表,范围从“特殊池”到“杂项检查”。实际上,除了“低资源模拟”之外,我们都选择了它们。低资源模拟正是这样一种设置,因此我们不希望重新启动并让任何设备驱动程序实际测试其行为,因为它的资源是模拟的,并且正在被有目的地耗尽。“特殊池”选项将在本文后面讨论。让我们首先检查“强制IRQL检查”。

假设一个驱动程序接触到一段分页内存。IRQL当前处于被动级别,这是所有用户模式代码运行的IRQL。但是,由于驱动程序已经接触到一块内存,内存管理器必须将该数据引入物理RAM并将其连接到该分页虚拟地址。现在,驱动程序执行一个操作,使IRQL上升到DISPATCH_LEVEL,并立即再次引用同一个分页缓冲区。要想让它显示为被操作系统捕获的bug,内存管理器需要在第一次引用、IRQL提升和第二次引用之间的少量指令发生的很短时间内,决定是否需要重用(或发送到分页文件)。这是极不可能的。“强制IRQL检查”所做的是,在启用该选项验证驱动程序时,它会导致IRQL移动到分派级别或更高级别。内存管理器获取连接到分页池虚拟内存的系统工作集的所有页,并断开这些页与物理内存的连接。

请注意,工作集是分配给进程的物理内存量,该量由内存管理器确定,因为操作系统已根据内存需求和分页速率监视该进程的行为。所以现在,如果这个驱动程序在DISPATCH_LEVEL再次访问这个缓冲区,它将生成一个页面错误,因为内存管理器将不得不去修复那个虚拟内存和那个物理内存之间的连接。此时内存管理器将检查当前的IRQL。它将看到它是DISPATCH_LEVEL或更高级别,然后确定这是一个非法操作,从而导致系统崩溃。这就是你想要的。您希望找到哪个驱动程序有足够的错误,以至于在执行某些非法操作时被抓到,这些操作将使系统在某些最终用户的计算机上或在某些不受控制的环境中崩溃。强制IRQL检查将揭示哪种驱动程序确实存在这些类型的错误,并因此暴露罪魁祸首驱动程序。

池跟踪选项对于驱动程序内存泄漏非常有用。I/O验证和增强的I/O验证使操作系统驱动程序验证程序代码对驱动程序传递到的和驱动程序传递回系统的数据结构执行一些严格的检查。这种数据结构称为中断请求包(IRP)。IRP的结构有一些特殊的规则。它必须指向有效的结构。它必须有一组一致的值。因此,驱动验证器将在驱动程序对其进行操作后检查该包,以确保其仍处于一致状态。

要创建一组步骤来将无法分析的崩溃转换为可分析的崩溃,我们必须认识到某些崩溃与某些条件相关,这些条件由这些选项描述。因此,在这些选项后单击Next,选择“从列表中选择驱动程序”。不要选择“自动选择系统上安装的所有驱动程序”。此时,菜单将加载驱动程序列表。在该列表下面是一个按钮,说明“将当前加载的驱动程序添加到列表中”。也许,你知道有一个驱动程序是有问题的,你可能想添加。拖放Provider部分以隔离那些不是来自Microsoft的驱动程序,并快速清点。在选择这些可疑的驱动程序、对这些驱动程序启用验证器并重新启动后,查看系统是否崩溃。如果系统没有崩溃,则采取另一个步骤,选择所有未签名的驱动程序和/或第三方设备驱动程序,并在这些驱动程序上运行验证程序。如果系统没有崩溃,那么作为最后手段,对每个驱动程序运行驱动程序验证程序。但是,不要一蹴而就。一次选择大约10或20个驱动程序,启用验证程序,然后重新启动。如果选择所有配置了这些选项的驱动程序,则系统可能需要20分钟才能重新启动。系统的行为可能在短时间内看起来有所不同,但最终会进入稳定状态(如果您将此作为练习,而不是尝试将无法分析的崩溃转化为可分析的崩溃)。

使用Notmyfault.exe测试驱动程序

目前受雇于微软的Mark Russonivich编写了一个名为“Notmyfault.exe”的测试驱动程序。此实用程序包含一个设备驱动程序myfault.sys,它将导致符合特定操作系统条件的特定类型的崩溃。在他编写的其他工具中,这个工具尤其是对于使用和理解系统崩溃以及如何避免它们是非常宝贵的。虽然不是必需的,但此工具最好在虚拟环境中运行。虚拟环境是作为计算环境运行的软件层。如果您下载了一个VMWare Workstation的试用版并安装了它,您将能够在正在运行的操作系统内(但与该操作系统分离)安装一个操作系统。试试看,安装一个旧版本的XP。

 

即使您不关心性能,但如果您的目标是所有驱动程序,那么还有另一个原因要追求成批的驱动程序,这就是特殊池选项。当我们调用NotmyFault.exe程序将控制请求发送到myfault.sys驱动程序以执行缓冲区溢出时,myfault.sys驱动程序将从内核内存分配一个缓冲区,然后写入缓冲区数组的末尾。这将损坏内存,如图所示:

 

注意,我们检查了缓冲区溢出选项。当我们按下“DoBug”按钮时,一些随机缓冲区将在内核内存中被覆盖。因此,被覆盖的内存已损坏。但是,仅仅损坏内存可能会导致系统崩溃,也可能不会,直到引用了损坏内存的内容。然后,系统就会崩溃。因此,在内存损坏和检测到损坏之间可能有很长的延迟。通常,另一个驱动程序或内核进行引用。MyFault.sys分配一个非分页的池缓冲区,并在末尾写入一个字符串,从而损坏池头和后面的数据结构。所以,我们按一次“Do bug”,什么也没发生。也许,按那个按钮十次,还是什么都没发生。不过,有一点是肯定的,我们现在有一个非常病态的内核内存。如果仍然没有崩溃,那么运行类似于Internet Explorer的程序,看看它是否引用了足以检测内存损坏的操作系统。如果这不起作用,那么运行一些更重的程序,更有可能导致系统崩溃,比如WindowsMessenger。
假设系统现在崩溃了。但当系统崩溃时,是不是Windows Messenger导致了系统崩溃?当然没有。但是,在一些内核模式的软件中发生了一些事情(不是Windows Messenger),这些事情被间接调用,导致系统崩溃。当内核检测到损坏的池时,蓝屏显示启用驱动程序验证程序。它告诉你发生了什么,但不是为什么。此时,我们将检查崩溃文件。崩溃文件显示了堆栈,但随后在跟踪堆栈上显示了一个Microsoft设备驱动程序,这可能是一个非常重要的驱动程序。但是,驱动程序只引用了损坏的内存。它实际上并没有损坏内存,这就是系统崩溃的原因。
现在,我们执行相同的测试,但是启用了驱动程序验证程序,启用了所有选项(特别是特殊池,但再次声明,不启用低资源模拟)。当您按下“DoBug”按钮时,驱动程序将尝试写入到其分配的末尾,但会在实际操作中被捕获。系统立即崩溃,但更重要的是,它直接指向myfault.sys。也就是说,我们当场抓到一名司机。触发的验证选项是特殊池选项。当使用特殊池验证选项集验证驱动程序时,Windows会尝试从内存的特殊区域满足对它的内存分配,因此命名为特殊池。此区域是特殊的,因为此区域中的每个其他页都是无效的内存页,因为它将驱动程序缓冲区与分配缓冲区的内存顶部对齐。所以,当司机从缓冲区的末端溜走时,它不会坐在其他缓冲区中,但最终会碰到这些无效的内存页中的一页。而且,仅仅是它接触到一个无效的内存页就触发了一个页面错误。页面错误处理程序查看引用的内容,发现从内核模式访问的内存页无效,并将立即使系统崩溃并告诉您驱动程序有问题:

 

但是,有两个条件,即使您在特定驱动程序或系统范围内启用了特殊池。发送到特殊池的分配必须略少于一页。也就是说,在x86系统上,分配的页大小必须小于4kb或4096字节。所以,当一个驱动程序进行大的内存分配时,它不会来自特殊的池。这意味着,如果它覆盖了缓冲区,它就没有myfault.sys演示中的保护和检查。特殊池是不需要重新启动的驱动程序验证选项之一,但请记住它是一个有限的资源。因此,当特殊池用完时,正在验证的驱动程序将把它们的池分配发送到普通池。换言之,它们将在没有上述保护的情况下进行验证。
另一个有用的测试是系统代码覆盖测试。当驱动程序中有一个错误损坏指针,并且指针指向操作系统内核或其他引导启动驱动程序的代码时,系统代码覆盖就会发生。大多数情况下,这种访问不会被检测到。在这些情况下,当它被检测到时,Windows将识别出一个驱动程序正在试图覆盖操作系统或另一个驱动程序的部分代码。要做到这一点,Windows必须有一个称为系统代码写入保护的工具。系统代码写入保护是内存管理器将操作系统和驱动程序的代码页标记为只读的机制。因此,如果驱动程序试图写入这些页面,则会触发页面错误,内存管理器会用停止代码来指示驱动程序试图修改代码,从而使系统崩溃。但是,由于性能原因,系统代码写入功能被关闭。也就是说,为了提高性能,内核在大多数系统上没有标记为只读。Windows为了节省CPU上作为缓存的translation look aside缓冲区的空间,将虚拟地址映射到物理地址,将操作系统代码和引导启动驱动程序映射到一个大的内存页中。在典型的x86机器上,标准或“小”页是4KB。但是,像驱动程序或内核这样的映像是在4kb的段中定义的,其中代码和数据在映像中。如果操作系统将包含代码页和数据页的整个映像加载到一个4 MB的大页中,则它别无选择,只能将该页上的内存保护设置为读/写。否则,内核和驱动程序将无法修改它们自己的数据(也映射到那个大页面)。因此,系统几乎总是设置为关闭系统代码覆盖。

简短的说明

一个错误的想法是,如果像记事本这样的可执行文件被启动,整个image就会被加载。实际上,只加载其中的一部分,这被称为“惰性分配器”。随着更多功能的使用,这个可执行文件的更多image将从磁盘中读取。必要的dll也不会全部加载。只加载这些dll的引用部分。这称为“虚拟分配”。Windows中任何可共享的内存都是共享的。这意味着代码和dll,但不是数据。一种误解是,如果加载两个记事本实例,则会有两个加载的记事本image。Windows意识到有一个映像的第二个实例已经将它的一部分加载到物理RAM中,并自动将两个虚拟映像连接到相同的底层页面。但是,在每个加载的记事本实例中键入的数据是对应实例的私有数据。因此,数据不是共享的,但是执行记事本的代码是共享的,dll也是共享的。
有一种方法可以在注册表中显式启用系统代码覆盖,但这不是必需的。当验证器打开时,即使使用最小的设置,也会启用系统代码覆盖。因此,当您打开verifier时,内核将用小页面映射自身和驱动程序,因此尝试写入代码将立即生成蓝屏。如果在Notmyfault.exe上选择“代码覆盖”单选按钮,myfault.sys将覆盖NtReadFile的前几个字节,NtReadFile是一个常用的系统函数。NtReadFile是代表从任何文件句柄读取的线程调用的底层系统调用。
因此,当我们按下“Do bug”时,代码将被覆盖(因为这是允许的,因为我们运行的是一个默认系统,它的内核和引导驱动程序映像映射到一个标记为读/写的大页面)。当代码被重写时,它将很容易被检测到,因为其他东西将调用NtReadFile函数,该函数将运行到某些被重写的指令中,从而导致崩溃。如果你看一下停止代码,就没有指向驱动程序的指针,因为导致这次崩溃的驱动程序早已不复存在。如果我们查看崩溃转储文件,我们可以很容易地找到一个关键系统组件驱动程序,如Win32k.sys,现在这是一个误诊。我们可以使用最高级的调试命令,并保留一个无法分析的崩溃文件。同样,解决方案是使用驱动程序验证程序。在驱动程序验证程序打开的情况下按“Do bug”表示系统代码覆盖已打开。当它崩溃时,蓝屏立即指向myfault.sys,并且进一步的文本说明试图写入只读内存。崩溃转储文件以前指向与崩溃无关的驱动程序,现在指向正确的驱动程

 

粗略地说,当Windows崩溃转储文件从调试器的分析引擎中不正确时,目标是将这些文件转换为可分析的文件。驱动验证器是帮助实现这一点的工具,以及提高系统性能的工具。

简介

不久前,我必须建立一个符号服务器,带有源索引,那时,关于这个主题的信息不多,所以我很难让所有的东西按照我们想要的方式工作。不幸的是,仍然只有这些相同的信息,但是自从微软将源代码发布到.NET框架并自动将其符号服务器添加到Visual Studio 2010以来,似乎有更多的人意识到了这一点。大多数使用.NET框架的人现在都会意识到,他们可以通过使用Microsoft的符号服务器从Microsoft获取符号文件和源代码来调试框架,但是有多少人真正了解什么是符号服务器,或者Visual Studio如何将源代码获取到.NET框架?答案出人意料地简单,与一些人的看法相反,他们认为这项技术已经存在了似乎是永远的,而且可以供你使用。因此,本文将向您介绍符号服务器的奇迹,也许更重要的是,源索引。

什么是符号服务器?

符号服务器基本上只是一个使用文件系统的非常简单的数据库,用于存储符号文件的不同版本。WinDbg和Visual Studio都可以通过SymSrv DLL使用这些数据库,SymSrv DLL随Windows调试工具包提供,用于为正在调试的应用程序加载匹配的符号。实际上,它不是一个数据库,而是一组结构方便的文件夹和文件,因此,您可以自由地复制和粘贴您的数据库到任何您喜欢的位置,但symbol服务器仍会记录事务,并保留已添加或从数据库中删除的所有内容的记录,因此您不能只是手动添加据我所知的档案。关于symbol服务器的一个重要注意事项是它不支持同时执行多个事务,没有锁定机制来阻止其他人在事务进行时更新数据库,因此您可能需要小心,一次只有一个人在更新数据库。
使用符号服务器意味着每个人都可以轻松访问最新版本的符号,如果需要调试较旧版本的应用程序或库,则无需担心自己会挖不出符号文件,它们将自动加载。
符号服务器本身是非常方便的,因为它允许您在调试时查看调用堆栈和其他有用的信息。但是举例来说,您正在调试一个客户机通过加载一个密钥库而经历的崩溃,并且您机器上的源代码不再匹配用于构建该应用程序特定版本的源代码,您可能很难准确地找到导致崩溃的原因;这是源索引出现的地方。

什么是源索引?

源索引是将命令嵌入到符号文件中的行为,当运行时,将从源代码管理系统中提取源代码的正确版本,或者从您可能已经准备好的其他备份中获取源代码。调试器可以在需要打开文件时运行这些命令,以便获得正确的源文件。因此,当您加载客户端在愤怒的电子邮件中发送给您的小型转储文件时,您可以在Visual Studio中加载它,您将看到用于生成该生成文件的确切源代码,以及(希望)指向有问题的代码行的大箭头所遇到的错误。

如何设置符号服务器

symbol服务器的先决条件是您有一些网络位置来存储数据库,即使您是唯一的用户,它也可以只是硬盘上的一个文件夹。下一步是告诉调试器符号服务器的位置,以便它在调试时检查符号文件。
在最新版本的Visual Studio中,您需要转到“工具”->“选项”->“调试”->“符号”,然后将路径添加到符号服务器。如果符号服务器位于某个网络上,则还应为Visual Studio指定一个本地缓存,以便在下次需要这些符号时将这些符号复制到该网络,而无需从该网络下载这些符号。

 

 

在旧版本的Visual Studio中,仅仅指定符号服务器的路径是不够的,还需要告诉它它是符号服务器。您可以将SRV*放在符号服务器路径之前。
SRV*只是symsrv*symsrv.dll*的简写,所以如果您看到完整版本,它的意思完全相同。SRV*语法有几个变体:

SRV*LocalCache*SymbolServerPath
SRV*LocalCache*NetworkCache*SymbolServerPath 

因此,可以为每个符号服务器指定不同的缓存位置。如果你是从某个非现场位置获取你的符号,你也可以指定一个网络缓存,这样当其他用户需要符号文件时,他们只需要直接从你自己的网络下载,而不是从地球的另一边下载。

如果使用WinDbg,则要添加符号服务器,需要转到“文件”->“符号文件路径”,然后使用上面的SRV*语法添加符号服务器。

 

 

您还可以为符号服务器设置一个环境变量,以便Visual Studio和WinDbg(以及任何其他兼容的调试器)都知道您的服务器,而不必在每个应用程序中显式地设置它们。需要创建的环境变量是符号路径,可以在中作为用户或系统变量创建。它对每台服务器使用SRV*语法,如果需要指定多台服务器,则需要用分号分隔每台服务器。

_NT_SYMBOL_PATH= SRV*c:\symbols*\\symbolserver;
SRV*c:\symbols*http://www.someotherplace.co.uk/symbols 

此时,您应该让调试器在服务器中查找符号,此时该符号可能为空。要向服务器添加符号,需要使用Windows调试工具提供的SymStore.exe。向服务器添加一组符号的基本命令是:

symstore add /f "c:\MyProject\Output\*.*" /s "\\MySymbolServer\Symbols" /t "MyProject"
/v "Build 1234" /c "Example Transaction"

关于每个命令的注释:

  • add告诉symstore我们正在添加文件。
  • /f对于我们要添加的文件(在本例中是一个或多个文件)的路径,如果您像我在这里所做的那样指定一个路径,则它将搜索要添加到服务器的任何兼容文件—这些文件包括Visual Studio生成的调试pdb文件以及二进制文件本身。请记住,如果要从小型转储进行调试,则可能还必须将二进制文件添加到符号服务器。我不知道如何指定要从中添加的多个路径,因此如果您只需要.PDB和.DLL文件(但不需要可执行文件),则必须对每个文件运行单独的命令,或者将要备份的所有文件移动到同一文件夹。
  • /s要将文件添加到的符号服务器的路径。如果只是一个空文件夹,那么它将添加必要的文件和文件夹,将空文件夹转换为符号服务器。
  • /t事务的名称,这是一个必需的参数,通常您只需将项目名称放在此处,或任何其他标识字符串。
  • /v要添加的文件的版本号。它不是必需的,只是为了您的方便,所以如果您需要手动找到一组特定的符号,您可以。
  • /c事务的注释,同样地,它不是必需的,只是用于日志文件和您的利益。

Symstore还有一些其他参数,允许您以几种不同的方式设置symbol服务器。我不在这里介绍它们,因为这里有一个很好的MSDN页面来解释它们,您可以从文章底部的链接中找到它们。

如何索引符号文件

在将符号文件添加到服务器之前,可以在其中嵌入命令,以便从版本控制系统或其他任何相关的位置提取当前源代码。Windows调试工具中包含一些脚本,这些脚本将把不同版本控制系统中的源代码索引到PDB文件中。我将给出一个如何使用脚本的快速示例,然后我将检查实际发生的情况,以便如果您有需要,可以编写自己的脚本。
要使用源索引脚本,首先需要安装Perl,因为这些实际上是Perl脚本。一旦您构建了项目,并希望用附加信息为PDB文件编制索引,调试器将需要从版本控制系统中提取源代码,您需要转到Windows安装调试工具中的srcsrv文件夹,并找到版本控制的相关脚本,例如,您正在使用Subversion,那么您需要运行svnindex.cmd。有两个参数需要传递给脚本,以便脚本可以索引文件,它们是指向项目工作目录的源路径的分号分隔列表,以及PDB文件所在文件夹的分号分隔列表。所以你的命令是:

svnindex.cmd /source="c:\SharedModules;C:\MyVeryImportantProject"
/symbols="c:\SharedModules\Release;c:\MyVeryImportantProject\Release"

然后,脚本将插入PDB文件中列出的每个文件的命令,以从SVN中提取这些命令。在SVN的情况下,如果需要使用特定的用户名和密码,还可以传入/user=“MyUserName”/pass=“MyPassword”注意,这些参数是特定于SVN脚本的,其他脚本可能不总是接受用户名和密码,并且可能有自己的特定设置。与控制台中的大多数内容一样,您可以通过传递-?作为论据。
每个索引脚本还支持从两个环境变量加载一个变量以及一个名为SrcSrv.ini的配置文件。运行脚本时,它们将使用大多数本地设置,因此命令行参数将覆盖srcsrv.ini,后者将覆盖环境变量。还可以通过添加/ini=“Path to ini file”指定运行脚本时要使用的特定配置文件。

脚本里做了什么

基本上,索引脚本的目的是生成这样的数据块:

SRCSRV: ini ------------------------------------------------
VERSION=1
INDEXVERSION=2
VERCTRL=Test
DATETIME=Mon, 04 October 2010
SRCSRV: variables ------------------------------------------
SRCSRVTRG=%targ%\%var4%\%var2%\%fnfile%(%var1%)
SRCSRVCMD=cmd /c copy "%var1%" "%SRCSRVTRG%"
SRCSRV: source files ---------------------------------------
D:\Documents\SKProjects\AlphaForms\AlphaForms\LayeredWindow.cs*11*_*AlphaForms\AlphaForms
*svn://192.168.1.5/AlphaForms/trunk/AlphaForms/LayeredWindow.cs
SRCSRV: end ------------------------------------------------

上面有一些关于如何构建命令的信息,下面列出了每个文件的该命令的参数。因此,如果该命令是一个简单的副本,那么您可以将该命令设置为:

copy "%var1%" "%srcsrvtrg%

var1引用变量列表中的第一项,在本例中为D:\ Documents\SKProjects\AlphaForms\AlphaForms\LayeredWindow.cs。srcsrvtrg是数据块头中指定文件复制位置的命名变量之一。在调用命令之前,调试器将检查文件是否存在,因此,如果您在此之前打开了该文件的版本,则不需要重新运行一个可能非常慢的命令。srcsrvtrg由其他几个命名变量组成。targ是文件将放入的本地缓存目录,fnfile实际上是一个函数,它从以下括号中指定的路径(在本例中是var1,文件的路径)获取文件名。
您可能已经注意到,两个模符号之间的所有内容都被视为变量,并将被它们表示的实际数据替换(如果可能的话),这种替换值的操作也是递归的,就像srcsrvtrg变量将被它表示的字符串替换一样,然后填写变量targ和var1。
事实上,将这些数据插入PDB文件是完全无用的,因为它只会将您系统中已有的文件复制到其他地方。一个更现实的例子是:

SRCSRV: ini ------------------------------------------------
VERSION=1
INDEXVERSION=2
VERCTRL=Subversion
DATETIME=Mon, 04 October 2010
SRCSRV: variables ------------------------------------------
SRCSRVTRG=%targ%\%var4%\%var2%\%fnfile%(%var1%)
SRCSRVCMD=cmd /c "svn cat "%var5%@%var2% --non-interactive > "%SRCSRVTRG%"
SRCSRV: source files ---------------------------------------
D:\Documents\SKProjects\AlphaForms\AlphaForms\LayeredWindow.cs*11*_*AlphaForms\AlphaForms
*svn://192.168.1.5/AlphaForms/trunk/AlphaForms/LayeredWindow.cs
D:\Documents\SKProjects\AlphaForms\AlphaForms\AlphaForm_WndProc.cs*10*_
*AlphaForms\AlphaForms*svn://192.168.1.5/AlphaForms/trunk/AlphaForms/AlphaForm_WndProc.cs
D:\Documents\SKProjects\AlphaForms\AlphaForms\AlphaForm.cs*10*_
*AlphaForms\AlphaForms*svn://192.168.1.5/AlphaForms/trunk/AlphaForms/AlphaForm.cs
SRCSRV: end ------------------------------------------------

然后,实际的命令将由main部分中指定的变量生成,因此对于第一个文件,它将如下所示:

cmd /c "svn cat " svn://192.168.1.5/AlphaForms/trunk/AlphaForms/LayeredWindow.cs@11
--non-interactive > "C:\Documents…"

然后将执行此命令,并希望将所需的文件放在目标目录中,然后调试器将尝试打开它。
如果您确实想编写自己的脚本或程序来索引PDB文件,那么您只需要生成一个类似的数据块,然后使用pdbstr.exe将其插入PDB文件。将自己的源代码编制索引的基本步骤将是这样的:

  1. 收集工作目录中的文件列表
  2. 获取提取命令的每个文件的参数列表
  3. 使用srctool获取PDB中引用的文件列表
  4. 将数据块的头写入某个临时文件
  5. 对于PDB中的每个文件,将参数添加到临时文件
  6. 使用pdbstr将数据插入PDB

要获取PDB中引用的文件列表,请使用:

srctool.exe "path to pdb file" –r 

它将把pdb中的每个文件打印到一个新的行上。要将数据块添加到PDB文件,需要使用:

pdbstr –w –p:"path to pdb file" –s:srcsrv –i:"path to temp file" 

-w开关指定您正在写入文件,使用-r会将数据流(如果存在)打印到控制台。-s给出了我们要写入的数据流的名称,在本例中是srcsrv。实际上,您可以使用任何喜欢的流名称将任何需要的内容插入到PDB文件中,但是Visual Studio将在srcsrv流中查找数据。-我给出了将插入PDB文件的输入文件的路径,在您的情况下,该文件就是您将数据写入的文件。

简介

此表旨在帮助将Windows运行时应用程序错误代码交叉引用到Microsoft.NET标准异常,这些异常可以作为应用程序异常处理技术的一部分。

对照表

.NET Exception (Namespace)

HRESULT(s) - symbolic

HRESULT(s) - raw

AccessViolationException

E_POINTER

0x80004003

AmbiguousMatchException (System.Reflection)

COR_E_AMBIGUOUSMATCH

0x8000211d

ApplicationException

COR_E_APPLICATION

0x80131600

AppDomainUnloadedException

COR_E_APPDOMAINUNLOADED

0x80131014

ArithmeticException

COR_E_ARITHMETIC

0x80070216

ArgumentException

COR_E_ARGUMENT

0x80070057

ArgumentNullException

E_POINTER

0x80004003

ArgumentOutOfRangeException

COR_E_ARGUMENTOUTOFRANGE

0x80131502

ERROR_NO_UNICODE_TRANSLATION

0x80070459

ArrayTypeMismatchException

COR_E_ARRAYTYPEMISMATCH

0x80131503

BadImageFormatException

COR_E_BADIMAGEFORMAT

0x8007000b

CLDB_E_FILE_OLDVER

0x80131107

CLDB_E_INDEX_NOTFOUND

0x80131124

CLDB_E_FILE_CORRUPT

0x8013110e

COR_E_NEWER_RUNTIME

0x8013101b

COR_E_ASSEMBLYEXPECTED

0x80131018

ERROR_BAD_EXE_FORMAT

0x800700c1

ERROR_EXE_MARKED_INVALID

0x800700c0

CORSEC_E_INVALID_IMAGE_FORMAT

0x8013141d

ERROR_NOACCESS

0x800703e6

ERROR_INVALID_ORDINAL

0x800700b6

ERROR_INVALID_DLL

0x80070482

ERROR_FILE_CORRUPT

0x80070570

COR_E_LOADING_REFERENCE_ASSEMBLY

0x80131058

META_E_BAD_SIGNATURE

0x80131192

CannotUnloadAppDomainException

COR_E_CANNOTUNLOADAPPDOMAIN

0x80131015

ContractException (System.Diagnostics.Contracts)

COR_E_CODECONTRACTFAILED

0x80131542

ContextMarshalException (System)

COR_E_CONTEXTMARSHAL

0x80131504

CustomAttributeFormatException (System.Reflection)

COR_E_CUSTOMATTRIBUTEFORMAT

0x80131605

CryptographicException (System.Security.Cryptography)

CORSEC_E_CRYPTO

0x80131430

CryptographicUnexpectedOperationException (System.Security.Cryptography)

CORSEC_E_CRYPTO_UNEX_OPER

0x80131431

DataMisalignedException

COR_E_DATAMISALIGNED

0x80131541

DirectoryNotFoundException (System.IO)

COR_E_DIRECTORYNOTFOUND

0x80070003

STG_E_PATHNOTFOUND

0x80030003

CTL_E_PATHNOTFOUND

0x800a004c

DivideByZeroException

COR_E_DIVIDEBYZERO

0x80020012

CTL_E_DIVISIONBYZERO

0x800a000b

DllNotFoundException

COR_E_DLLNOTFOUND

0x80131524

DuplicateWaitObjectException

COR_E_DUPLICATEWAITOBJECT

0x80131529

EndOfStreamException (System.IO)

COR_E_ENDOFSTREAM

0x80070026

EntryPointNotFoundException

COR_E_ENTRYPOINTNOTFOUND

0x80131523

Exception

COR_E_EXCEPTION

0x80131500

ExecutionEngineException

COR_E_EXECUTIONENGINE

0x80131506

FieldAccessException

COR_E_FIELDACCESS

0x80131507

FileLoadException (System.IO)

COR_E_FILELOAD

0x80131621

FUSION_E_INVALID_PRIVATE_ASM_LOCATION

0x80131041

FUSION_E_SIGNATURE_CHECK_FAILED

0x80131045

FUSION_E_LOADFROM_BLOCKED

0x80131051

FUSION_E_CACHEFILE_FAILED

0x80131052

FUSION_E_ASM_MODULE_MISSING

0x80131042

FUSION_E_INVALID_NAME

0x80131047

FUSION_E_PRIVATE_ASM_DISALLOWED

0x80131044

FUSION_E_HOST_GAC_ASM_MISMATCH

0x80131050

COR_E_MODULE_HASH_CHECK_FAILED

0x80131039

FUSION_E_REF_DEF_MISMATCH

0x80131040

SECURITY_E_INCOMPATIBLE_SHARE

0x80131401

SECURITY_E_INCOMPATIBLE_EVIDENCE

0x80131403

SECURITY_E_UNVERIFIABLE

0x80131402

COR_E_FIXUPSINEXE

0x80131019

ERROR_TOO_MANY_OPEN_FILES

0x80070004

ERROR_SHARING_VIOLATION

0x80070020

ERROR_LOCK_VIOLATION

0x80070021

ERROR_OPEN_FAILED

0x8007006e

ERROR_DISK_CORRUPT

0x80070571

ERROR_UNRECOGNIZED_VOLUME

0x800703ed

ERROR_DLL_INIT_FAILED

0x8007045a

FUSION_E_CODE_DOWNLOAD_DISABLED

0x80131048

CORSEC_E_MISSING_STRONGNAME

0x8013141b

MSEE_E_ASSEMBLYLOADINPROGRESS

0x80131016

ERROR_FILE_INVALID

0x800703ee

FileNotFoundException (System.IO)

ERROR_FILE_NOT_FOUND

0x80070002

ERROR_MOD_NOT_FOUND

0x8007007e

ERROR_INVALID_NAME

0x8007007b

CTL_E_FILENOTFOUND

0x800a0035

ERROR_PATH_NOT_FOUND

0x80070003

ERROR_BAD_NET_NAME

0x80070043

ERROR_BAD_NETPATH

0x80070035

ERROR_NOT_READY

0x80070015

ERROR_WRONG_TARGET_NAME

0x80070574

INET_E_UNKNOWN_PROTOCOL

0x800c000d

INET_E_CONNECTION_TIMEOUT

0x800c000b

INET_E_CANNOT_CONNECT

0x800c0004

INET_E_RESOURCE_NOT_FOUND

0x800c0005

INET_E_OBJECT_NOT_FOUND

0x800c0006

INET_E_DOWNLOAD_FAILURE

0x800c0008

INET_E_DATA_NOT_AVAILABLE

0x800c0007

ERROR_DLL_NOT_FOUND

0x80070485

CLR_E_BIND_ASSEMBLY_VERSION_TOO_LOW

0x80132000

CLR_E_BIND_ASSEMBLY_PUBLIC_KEY_MISMATCH

0x80132001

CLR_E_BIND_ASSEMBLY_NOT_FOUND

0x80132004

FormatException

COR_E_FORMAT

0x80131537

IndexOutOfRangeException

COR_E_INDEXOUTOFRANGE

0x80131508

InsufficientExecutionStackException

COR_E_INSUFFICIENTEXECUTIONSTACK

0x80131578

InvalidCastException

COR_E_INVALIDCAST

0x80004002

InvalidComObjectException (System.Runtime.InteropServices)

COR_E_INVALIDCOMOBJECT

0x80131527

InvalidFilterCriteriaException (System.Reflection)

COR_E_INVALIDFILTERCRITERIA

0x80131601

InvalidOleVariantTypeException (System.Runtime.InteropServices)

COR_E_INVALIDOLEVARIANTTYPE

0x80131531

InvalidOperationException

COR_E_INVALIDOPERATION

0x80131509

InvalidProgramException

COR_E_INVALIDPROGRAM

0x8013153a

IOException (System.IO)

COR_E_IO

0x80131620

CTL_E_DEVICEIOERROR

0x800a0039

IsolatedStorageException (System.IO.IsolatedStorage)

ISS_E_ISOSTORE

0x80131450

ISS_E_OPEN_STORE_FILE

0x80131460

ISS_E_OPEN_FILE_MAPPING

0x80131461

ISS_E_MAP_VIEW_OF_FILE

0x80131462

ISS_E_GET_FILE_SIZE

0x80131463

ISS_E_CREATE_MUTEX

0x80131464

ISS_E_LOCK_FAILED

0x80131465

ISS_E_FILE_WRITE

0x80131466

ISS_E_SET_FILE_POINTER

0x80131467

ISS_E_CREATE_DIR

0x80131468

ISS_E_CORRUPTED_STORE_FILE

0x80131480

ISS_E_STORE_VERSION

0x80131481

ISS_E_FILE_NOT_MAPPED

0x80131482

ISS_E_BLOCK_SIZE_TOO_SMALL

0x80131483

ISS_E_ALLOC_TOO_LARGE

0x80131484

ISS_E_USAGE_WILL_EXCEED_QUOTA

0x80131485

ISS_E_TABLE_ROW_NOT_FOUND

0x80131486

ISS_E_DEPRECATE

0x801314a0

ISS_E_CALLER

0x801314a1

ISS_E_PATH_LENGTH

0x801314a2

ISS_E_MACHINE

0x801314a3

ISS_E_STORE_NOT_OPEN

0x80131469

ISS_E_MACHINE_DACL

0x801314a4

MarshalDirectiveException (System.Runtime.InteropServices)

COR_E_MARSHALDIRECTIVE

0x80131535

MethodAccessException

COR_E_METHODACCESS

0x80131510

META_E_CA_FRIENDS_SN_REQUIRED

0x801311e6

MemberAccessException

COR_E_MEMBERACCESS

0x8013151a

MissingFieldException

COR_E_MISSINGFIELD

0x80131511

MissingManifestResourceException (System.Resources)

COR_E_MISSINGMANIFESTRESOURCE

0x80131532

MissingMemberException

COR_E_MISSINGMEMBER

0x80131512

MissingMethodException

COR_E_MISSINGMETHOD

0x80131513

MulticastNotSupportedException

COR_E_MULTICASTNOTSUPPORTED

0x80131514

NotFiniteNumberException

COR_E_NOTFINITENUMBER

0x80131528

NotImplementedException

E_NOTIMPL

0x80004001

NotSupportedException

COR_E_NOTSUPPORTED

0x80131515

NullReferenceException

COR_E_NULLREFERENCE

0x80004003

ObjectDisposedException

COR_E_OBJECTDISPOSED

0x80131622

RO_E_CLOSED

0x80000013

OperationCanceledException

COR_E_OPERATIONCANCELED

0x8013153b

OutOfMemoryException

E_OUTOFMEMORY

0x8007000e

CTL_E_OUTOFMEMORY

0x800a0007

OverflowException

COR_E_OVERFLOW

0x80131516

CTL_E_OVERFLOW

0x800a0006

PathTooLongException (System.IO)

COR_E_PATHTOOLONG

0x800700ce

PlatformNotSupportedException

COR_E_PLATFORMNOTSUPPORTED

0x80131539

System.RankException

COR_E_RANK

0x80131517

ReflectionTypeLoadException (System.Reflection)

COR_E_REFLECTIONTYPELOAD

0x80131602

RemotingException (System.Runtime.Remoting)

COR_E_REMOTING

0x8013150b

RuntimeWrappedException (System.Runtime.CompilerServices)

COR_E_RUNTIMEWRAPPED

0x8013153e

ServerException (System.Runtime.Remoting)

COR_E_SERVER

0x8013150e

SecurityException (System.Security)

COR_E_SECURITY

0x8013150a

CORSEC_E_INVALID_STRONGNAME

0x8013141a

CTL_E_PERMISSIONDENIED

0x800a0046

CORSEC_E_INVALID_PUBLICKEY

0x8013141e

CORSEC_E_SIGNATURE_MISMATCH

0x80131420

SafeArrayRankMismatchException System.Runtime.InteropServices)

COR_E_SAFEARRAYRANKMISMATCH

0x80131538

SafeArrayTypeMismatchException (System.Runtime.InteropServices)

COR_E_SAFEARRAYTYPEMISMATCH

0x80131533

SerializationException (System.Runtime.Serialization)

COR_E_SERIALIZATION

0x8013150c

StackOverflowException

COR_E_STACKOVERFLOW

0x800703e9

CTL_E_OUTOFSTACKSPACE

0x800a001c

SynchronizationLockException (System.Threading)

COR_E_SYNCHRONIZATIONLOCK

0x80131518

SystemException

COR_E_SYSTEM

0x80131501

TargetException (System.Reflection)

COR_E_TARGET

0x80131603

TargetInvocationException (System.Reflection)

COR_E_TARGETINVOCATION

0x80131604

TargetParameterCountException (System.Reflection)

COR_E_TARGETPARAMCOUNT

0x8002000e

ThreadAbortException (System.Threading)

COR_E_THREADABORTED

0x80131530

ThreadInterruptedException (System.Threading)

COR_E_THREADINTERRUPTED

0x80131519

ThreadStateException (System.Threading)

COR_E_THREADSTATE

0x80131520

ThreadStartException (System.Threading)

COR_E_THREADSTART

0x80131525

TypeAccessException

COR_E_TYPEACCESS

0x80131543

TypeInitializationException

COR_E_TYPEINITIALIZATION

0x80131534

TypeLoadException

COR_E_TYPELOAD

0x80131522

RO_E_METADATA_NAME_NOT_FOUND

0x8000000f

CLR_E_BIND_TYPE_NOT_FOUND

0x80132005

TypeUnloadedException

COR_E_TYPEUNLOADED

0x80131013

UnauthorizedAccessException

COR_E_UNAUTHORIZEDACCESS

0x80070005

CTL_E_PATHFILEACCESSERROR

0x800a004b

VerificationException (System.Security)

COR_E_VERIFICATION

0x8013150d

PolicyException (System.Security.Policy)

CORSEC_E_POLICY_EXCEPTION

0x80131416

CORSEC_E_NO_EXEC_PERM

0x80131418

CORSEC_E_MIN_GRANT_FAIL

0x80131417

XmlSyntaxException (System.Security)

CORSEC_E_XMLSYNTAX

0x80131419

COMException (System.Runtime.InteropServices)

E_FAIL

0x80004005

ExternalException (System.Runtime.InteropServices)

E_FAIL

0x80004005

SEHException (System.Runtime.InteropServices)

E_FAIL

0x80004005

ElementNotAvailableException (Windows.UI.Xaml.Automation)

(none)

0x802b001f

ElementNotEnabledException (Windows.UI.Xaml.Automation)

(none)

0x802b001e

LayoutCycleException (Windows.UI.Xaml)

(none)

0x802b0014

XamlParseException (Windows.UI.Xaml.Markup)

(none)

0x802b000a

说明

在上表的“.NET异常”列中,如果链接了异常名称,则该异常是.NET for Windows运行时应用程序类的一部分。这意味着您可以在自己的代码中引发该类型的新异常。或者您可以捕获这些异常,特别是作为try-catch或UnhandledException异常处理的一部分。如果未链接异常名称,则该异常不属于.NET for Windows运行时类的一部分。不属于.NET for Windows运行时集的异常可能在某些互操作方案中遇到,也可能来自系统或Windows运行时内部。您将无法使用该特定异常类型为它们编写捕获块,因为您为Windows运行时应用程序运行的.NET库不知道该类型。但是您仍然可以读取一个HRESULT代码,从“HRESULT(s)-raw:”列中查找它,并注意到有一个对应的.NET异常。或者你可以把它当作一般的例外。然后,您可以阅读.NET文档,或许可以了解有关该异常的意图以及原始代码引发该异常的原因的更多信息,即使该异常未在.NET for Windows运行时类型中表示。如果没有为.NET异常列出命名空间,则它来自系统命名空间。

“HRESULT(s)-symbolic”列中列出的常量来自多种来源。有些是在winerror.h中定义的,有些是在特定于组件对象模型(COM)编程的头文件中定义的,有些是在属于Windows特定子系统的头文件中定义的。有些需要HRESULT_From_Win32宏用法,其中包含来自更早的常量集的代码(前面是ERROR_)。对于使用.NET语言时的典型Windows运行时编程,这些头文件不是作为项目的一部分提供的。如果您正在获取系统无法映射到标准异常的情况下的错误代码信息,则可能会将其视为原始整数或十六进制值,并且不会自动支持将数字代码别名为Windows命名的常量值。尽管如此,在以前的Windows错误报告系统的基础上,仍然存在按其命名常量而不是原始代码引用错误代码的历史。您可以使用表中的命名常量进一步研究其他文档源(如论坛或支持文档)中的错误,特别是该错误对桌面编程、Microsoft Win32和COM等意味着什么。

SystemException:在原始的.NET异常层次结构中,许多异常是从系统异常派生的。例如,从SystemException派生的System.ArgumentException。从SystemException继承表明.NET核心定义了异常。Windows运行时的.NET类集合中未包含系统异常。所有在完整框架下从SystemException派生的异常都是从System.Exception派生的。
COMException:NET文档建议对任何无法识别的HRESULT抛出COMException,但这不是Windows运行时应用的行为。相反,COMException通常是用于源于组件的未映射异常的标准异常。来自您自己的应用程序代码或系统的未映射异常报告为基本异常,其HResult值不标准。
ExternalException:不包含在.NET for Windows运行时中。对于确实存在的异常(如SEHException),在层次结构中看不到这一点。

 

 

今天,当我们继续学习.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。
为了说明这一点,我们将通过几个不同的例子。我已经包含了下面的完整代码示例以供参考,之后我们将更详细地探讨细节:

usingSystem;usingSystem.Reflection;usingUtility;namespaceAirbrake.BadImageFormatException
{
classProgram
{
static void Main(string[] args)
{
LoadingNonDotNetLibraryExample();
Logging.Log(
"-----------------");
DifferingCPUExample();
Logging.Log(
"-----------------");
OldDotNetExample();
}
private static voidLoadingNonDotNetLibraryExample()
{
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 voidDifferingCPUExample()
{
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 voidOldDotNetExample()
{
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);
}
}
}
}
usingSystem;usingSystem.Diagnostics;namespaceUtility
{
/// <summary> ///Houses all logging methods for various debug outputs./// </summary> public static classLogging
{
/// <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(objectvalue)
{
#if DEBUGDebug.WriteLine(value);#elseConsole.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 DEBUGDebug.WriteLine(value);#elseConsole.WriteLine(value);#endif}
}
}