2023年1月

如名称的Win32部分所示System.ComponentModel.Win32Exception仅在处理传统样式的应用程序或代码时发生-应用程序必须调用直接操作系统调用,例如尝试执行其他应用程序。在本文中,我们将深入探讨System.ComponentModel.Win32异常。更详细地说,包括它在.NET异常层次结构中的位置。我们还将深入研究一些函数C#代码示例,以更好地说明如何System.ComponentModel.Win32Exceptions可能会在自己的编码中被抛出!

要点

  • 所有 .NET exceptions 继承自 System.Exception base class, 或者继承自inherited class.
  • System.SystemException 继承自 System.Exception class.
  • System.Runtime.InteropServices.ExternalException 直接继承自System.SystemException.
  • 最后, System.ComponentModel.Win32Exception 继承自 System.Runtime.InteropServices.ExternalException.

什么时候用它呢?

这个System.ComponentModel.Win32异常是在使用内部win32样式的操作系统调用时发生错误时在.NET应用程序中发生的最基本的异常类型。这些问题可能不同,从无效路径和文件找不到错误到网络地址问题和资源管理问题。因为System.ComponentModel.Win32Exceptions是旧形式异常的包装器,您可能遇到的每个可能的错误都有自己的NativeErrorCode属性值,它是一个32位整数,引用与引发的异常相关联的Win32错误代码值。
我们的示例代码开始。整个代码段如下所示,之后我们将更详细地讨论它:

usingSystem.Diagnostics;usingUtility;namespaceAirbrake.ComponentModel.Win32Exception
{
classProgram
{
static void Main(string[] args)
{
StartProcessFromPath(
"c:/windows/notepad.exe");
Logging.LineSeparator();
StartProcessFromPath(
"c:/windows/invalid.exe");
}
static void StartProcessFromPath(stringpath)
{
try{//Create a new process with StartInfo.FileName set to provided path. var process = new Process { StartInfo = { FileName =path } };//Attempt to start the process using provided executable path. var success =process.Start();if(success)
{
Logging.Log($
"Successfully launched '{process.ProcessName.ToString()}' process!");//Sleep for two seconds to allow time for window to be shown. System.Threading.Thread.Sleep(2000);//Kill process. process.Kill();
Logging.Log($
"Killed '{process.ProcessName.ToString()}' process.");
}
else{//This code never executes since we're catching//an exception from the process.Start() invocation line. }
}
catch(System.ComponentModel.Win32Exception exception)
{
//Indicate failure to start. Logging.Log($"Unable to start process with executable path '{path}'.");//Output caught exception. Logging.Log(exception);
Logging.Log($
"Native Win32 Error Code: {exception.NativeErrorCode}");
}
}
}
}
usingSystem;usingSystem.Collections;usingSystem.Collections.Generic;usingSystem.Diagnostics;usingSystem.Reflection;usingSystem.Text;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(stringvalue)
{
#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}/// <summary> ///Outputs a dashed line separator to<see cref="System.Diagnostics.Debug.WriteLine"/> ///if DEBUG mode is enabled, otherwise uses standard<see cref="Console.WriteLine"/>./// </summary> public static voidLineSeparator()
{
#if DEBUGDebug.WriteLine(new string('-', 20));#elseConsole.WriteLine(new string('-', 20));#endif}
}
}

简介

STATUS_IN_PAGE_ERROR---页内故障,这是当Windows尝试从内存映射文件将数据读入RAM并发生阻止数据读取的I / O错误时发生的非常低级别的错误。当它发生在.exe时,如setup.exe程序,这是一个致命的错误,因为它无法执行无法读取的代码。表示0x%p处的指令引用了位于0x%p的内存。由于0x%x的I/O错误状态,未将所需数据放入内存。线程试图访问一个不存在的页面,并且系统无法加载页面。-即程序或内存映射文件无法调出,因为它不再可访问。设备如果读取出错,驱动程序可以返回此异常。其定义如下:

//
// MessageId: STATUS_IN_PAGE_ERROR
//
// MessageText:
//
// The instruction at 0x%p referenced memory at 0x%p. The required data was not placed into memory because of an I/O error status of 0x%x.
//
#define STATUS_IN_PAGE_ERROR             ((NTSTATUS)0xC0000006L)    // winnt

异常结构说明

ExceptionAddress: 03638c90 (libcef!v8::internal::CopyCharsUnsigned<unsigned char,unsigned short>+0x00000020)
   ExceptionCode: c0000006 (In-page I/O error)
  ExceptionFlags: 00000000
NumberParameters: 3
   Parameter[0]: 00000000
   Parameter[1]: 07b41828//IO操作失败的内存地址
   Parameter[2]: c000000e//具体IO操作错误
Inpage operation failed at 07b41828, due to I/O error c000000e

说明

应用程序有时从网络共享启动,一些客户在运行应用程序时报告了外部异常C0000006。根据我在谷歌的研究,这个“可能”与Image被调出和无法从网络重新加载有关。解决方法是通过设置IMAGE_FILE_NET_RUN_FROM_SWAP标志,告诉Windows将完整的映像文件加载到交换中并从那里运行它。我的应用程序还依赖于运行时加载的各种.bpl和.dll库。其中只有一部分可以由我更改,有些由其他供应商提供。如果exe设置了这个标志,这个库会发生什么?是否也加载到交换文件中,还是仍被调出并在需要时重新加载?我是否也需要在库中包含此标志?该标志仅适用于设置它的PE模块。因此,在EXE中设置标志并不意味着该EXE加载的模块会受到该标志的影响。由EXE加载的每个模块(DLL、包等)将由加载程序根据该模块中指定的PE选项进行处理。因此,您需要在网络共享上的每个模块上设置PE标志。

我还建议添加IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP。

 

COM方法通过返回HRESULT报告错误;.NET方法通过引发异常来报告错误。运行时处理两者之间的转换。.NET Framework中的每个异常类都映射到一个HRESULT。
用户定义的异常类可以指定任何适合的HRESULT。这些异常类可以通过在异常对象上设置HRESULT字段来动态更改生成异常时返回的HRESULT。有关异常的其他信息通过IErrorInfo接口提供给客户端,该接口在非托管进程的.NET对象上实现。如果创建一个扩展System.Exception,必须在构造期间设置HRESULT字段。否则,基类将分配HRESULT值。通过在异常的构造函数中提供值,可以将新的异常类映射到现有的HRESULT。

请注意,在线程上存在IErrorInfo的情况下,运行时有时会忽略HRESULT。在HRESULT和IErrorInfo不表示相同错误的情况下,可能会发生此行为。