clr20r3 system.InvalidOperationException 程序终止的几种解决方案

本文介绍如何在.NET Framework的不同环境中捕获和处理未处理异常,包括C/S、B/S架构及ActiveX应用。

    这是因为.NET Framework 1.0 和 1.1 这两个版本对许多未处理异常(例如,线程池线程中的未处理异常)提供支撑,而 Framework 2.0 版中,公共语言运行库允许线程中的多数未处理异常自然继续。在多数情况下,这意味着未处理异常会导致应用程序终止。

 

一、C/S 解决方案(以下任何一种方法)
1. 在应用程序配置文件中,添加如下内容:
<configuration>
  <runtime>
    <legacyUnhandledExceptionPolicy enabled="true" />
  </runtime> 
</configuration>

 

2. 在应用程序配置文件中,添加如下内容:
<configuration>
  <startup>
    <supportedRuntime version="v1.1.4322"/>
  </startup>
</configuration>

 

3. 使用Application.ThreadException事件在异常导致程序退出前截获异常。示例如下:
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlAppDomain)]
public static void Main(string[] args)
{
    Application.ThreadException += new ThreadExceptionEventHandler(ErrorHandlerForm.Form1_UIThreadException);
    Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
    AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

    Application.Run(new ErrorHandlerForm());
}

 

// 在主线程中产生异常
private void button1_Click(object sender, System.EventArgs e)
{
    throw new ArgumentException("The parameter was invalid");
}

 

// 创建产生异常的线程
private void button2_Click(object sender, System.EventArgs e)
{
    ThreadStart newThreadStart = new ThreadStart(newThread_Execute);
    newThread = new Thread(newThreadStart);
    newThread.Start();
}

 

// 产生异常的方法
void newThread_Execute()
{
    throw new Exception("The method or operation is not implemented.");
}

 

private static void Form1_UIThreadException(object sender, ThreadExceptionEventArgs t)
{
    DialogResult result = DialogResult.Cancel;
    try
    {
        result = ShowThreadExceptionDialog("Windows Forms Error", t.Exception);
    }
    catch
    {
        try
        {
            MessageBox.Show("Fatal Windows Forms Error",
                "Fatal Windows Forms Error", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Stop);
        }
        finally
        {
            Application.Exit();
        }
    }

    if (result == DialogResult.Abort)
        Application.Exit();
}

 

// 由于 UnhandledException 无法阻止应用程序终止,因而此示例只是在终止前将错误记录在应用程序事件日志中。
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
    try
    {
        Exception ex = (Exception)e.ExceptionObject;
        string errorMsg = "An application error occurred. Please contact the adminstrator " +
            "with the following information:/n/n";

        if (!EventLog.SourceExists("ThreadException"))
        {
            EventLog.CreateEventSource("ThreadException", "Application");
        }

        EventLog myLog = new EventLog();
        myLog.Source = "ThreadException";
        myLog.WriteEntry(errorMsg + ex.Message + "/n/nStack Trace:/n" + ex.StackTrace);
    }
    catch (Exception exc)
    {
        try
        {
            MessageBox.Show("Fatal Non-UI Error",
                "Fatal Non-UI Error. Could not write the error to the event log. Reason: "
                + exc.Message, MessageBoxButtons.OK, MessageBoxIcon.Stop);
        }
        finally
        {
            Application.Exit();
        }
    }
}

 

private static DialogResult ShowThreadExceptionDialog(string title, Exception e)
{
    string errorMsg = "An application error occurred. Please contact the adminstrator " +
        "with the following information:/n/n";
    errorMsg = errorMsg + e.Message + "/n/nStack Trace:/n" + e.StackTrace;
    return MessageBox.Show(errorMsg, title, MessageBoxButtons.AbortRetryIgnore,
        MessageBoxIcon.Stop);
}

 


二、B/S 解决方案(以下任何一种方法)
1. 在IE目录(C:/Program Files/Internet Explorer)下建立iexplore.exe.config文件,内容如下:
<?xml version="1.0"?>
<configuration>
  <runtime>
    <legacyUnhandledExceptionPolicy enabled="true" />
  </runtime> 
</configuration>

 

2. 不建议使用此方法,这将导致使用 framework 1.1 以后版本的程序在IE中报错。
建立同上的配置文件,但内容如下:
<?xml version="1.0"?>
<configuration>
  <startup>
    <supportedRuntime version="v1.1.4322"/>
  </startup>
</configuration>

 

3. 这个比较繁琐,分为三步:
⑴. 将下面的代码保存成文件,文件名为UnhandledExceptionModule.cs,路径是C:/Program Files/Microsoft Visual Studio 8/VC/
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Web;
 
namespace WebMonitor {
    public class UnhandledExceptionModule: IHttpModule {

        static int _unhandledExceptionCount = 0;

        static string _sourceName = null;
        static object _initLock = new object();
        static bool _initialized = false;

        public void Init(HttpApplication app) {

            // Do this one time for each AppDomain.
            if (!_initialized) {
                lock (_initLock) {
                    if (!_initialized) {
                        string webenginePath = Path.Combine(RuntimeEnvironment.GetRuntimeDirectory(), "webengine.dll");

                        if (!File.Exists(webenginePath)) {
                            throw new Exception(String.Format(CultureInfo.InvariantCulture,
                                                              "Failed to locate webengine.dll at '{0}'.  This module requires .NET Framework 2.0.",
                                                              webenginePath));
                        }

                        FileVersionInfo ver = FileVersionInfo.GetVersionInfo(webenginePath);
                        _sourceName = string.Format(CultureInfo.InvariantCulture, "ASP.NET {0}.{1}.{2}.0",
                                                    ver.FileMajorPart, ver.FileMinorPart, ver.FileBuildPart);

                        if (!EventLog.SourceExists(_sourceName)) {
                            throw new Exception(String.Format(CultureInfo.InvariantCulture,
                                                              "There is no EventLog source named '{0}'. This module requires .NET Framework 2.0.",
                                                              _sourceName));
                        }
 
                        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(OnUnhandledException);
 
                        _initialized = true;
                    }
                }
            }
        }

        public void Dispose() {
        }

        void OnUnhandledException(object o, UnhandledExceptionEventArgs e) {
            // Let this occur one time for each AppDomain.
            if (Interlocked.Exchange(ref _unhandledExceptionCount, 1) != 0)
                return;

            StringBuilder message = new StringBuilder("/r/n/r/nUnhandledException logged by UnhandledExceptionModule.dll:/r/n/r/nappId=");

            string appId = (string) AppDomain.CurrentDomain.GetData(".appId");
            if (appId != null) {
                message.Append(appId);
            }
           
            Exception currentException = null;
            for (currentException = (Exception)e.ExceptionObject; currentException != null; currentException = currentException.InnerException) {
                message.AppendFormat("/r/n/r/ntype={0}/r/n/r/nmessage={1}/r/n/r/nstack=/r/n{2}/r/n/r/n",
                                     currentException.GetType().FullName,
                                     currentException.Message,
                                     currentException.StackTrace);
            }          

            EventLog Log = new EventLog();
            Log.Source = _sourceName;
            Log.WriteEntry(message.ToString(), EventLogEntryType.Error);
        }
    }
}

 

⑵. 打开Visual Studio 2005的命令提示行窗口
输入Type sn.exe -k key.snk后回车
输入Type csc /t:library /r:system.web.dll,system.dll /keyfile:key.snk UnhandledExceptionModule.cs后回车
输入gacutil.exe /if UnhandledExceptionModule.dll后回车
输入ngen install UnhandledExceptionModule.dll后回车
输入gacutil /l UnhandledExceptionModule后回车并将显示的”强名称”信息复制下来

 

⑶. 打开ASP.net应用程序的Web.config文件,将下面的XML加到里面。注意:不包括”[]”,可能是添加到<httpModules></httpModules>之间。
<add name="UnhandledExceptionModule" type="WebMonitor.UnhandledExceptionModule, [这里换为上面复制的强名称信息]" />

 

 

三、微软并不建议的解决方案
    打开位于 %WINDIR%/Microsoft.NET/Framework/v2.0.50727 目录下的 Aspnet.config 文件,将属性 legacyUnhandledExceptionPolicy 的 enabled 设置为 true

 

 

四、跳出三界外——ActiveX
    ActiveX 的特点决定了不可能去更改每个客户端的设置,采用 B/S 解决方案里的第 3 种方法也不行,至于行不通的原因,我想可能是因为 ActiveX 的子控件产生的异常直接

被 CLR 截获了,并没有传到最外层的 ActiveX 控件,这只是个人猜测,如果有清楚的朋友,还望指正。
   

    最终,我也没找到在 ActiveX 情况的解决方法,但这却是我最需要的,无奈之下,重新检查代码,发现了其中的问题:在子线程中创建了控件,又将它添加到了主线程的 UI 上。
    以前遇到这种情况,系统就会报错了,这次居然可以蒙混过关,最搞不懂的是在 framework 2.0 的 C/S 结构下也没有报错,偏偏在 IE(ActiveX) 里挂了。唉,都是宿主惹的祸。

    嘿嘿,不想承担责任,就得找个顶罪的 :)

 

 

注:① 之所以说可能,是因为 MSDN 上没有写具体加到配置文件的哪个位置,我从其它的配置文件中看到类似的信息加到了<httpModules>里,因为这种方案没能解决我的问题,所以我并不确定是不是加到这个位置。

 


 

<think>我们正在解决一个特定的异常:System.InvalidOperationException,发生在System.Console.dll中,错误信息为“Cannot read keys when either application does not have a console or when console input has been redirected. Try Console.Read.” 根据引用[1][2][3],这个异常通常发生在两种情况下: 1. 应用程序没有控制台(例如,它是一个GUI应用程序,或者是一个Web应用程序)。 2. 控制台输入被重定向(例如,从文件读取输入而不是键盘)。 引用[2]提到,使用ReadLine是可行的,但ReadKey会抛出异常。因此,我们需要避免在不支持控制台键盘读取的环境中使用Console.ReadKey()。 解决方案: 1. 检查应用程序类型:确保你正在运行的是一个控制台应用程序,并且拥有控制台窗口。如果是Windows应用程序(如WinForms或WPF),则没有控制台,除非你显式地分配了一个控制台(通过AllocConsole等API)。 2. 避免在输入被重定向时使用ReadKey:例如,如果你使用管道(pipe)将另一个程序的输出作为输入,或者从文件重定向输入,那么控制台输入流就不是来自键盘,此时ReadKey会失败。 3. 替代方法:使用Console.Read()或Console.ReadLine()来读取输入。但是注意,这些方法在遇到重定向时也可能返回null或需要处理不同的行为。 4. 检测控制台是否存在:在执行Console.ReadKey()之前,先进行检查。 具体检查方法: 我们可以使用Console.IsInputRedirected属性(.NET Framework 4.5+)或者Console.KeyAvailable属性(但注意在输入重定向时KeyAvailable也会抛出异常)来避免。 然而,在.NET Framework 4.5及以上版本,推荐的做法是: - 使用Console.IsInputRedirected判断输入是否被重定向。 - 使用Console.In(标准输入流)检查是否可用。 但是,从异常信息来看,除了重定向,应用程序没有控制台也会导致这个异常。我们可以使用Console.IsInputRedirected和Console.IsOutputRedirected等属性,但并没有直接检查“是否有控制台”的属性。 另一种方法是使用P/Invoke来检查是否有控制台。但更简单的做法是使用try-catch。 建议的代码修改: 如果你确实需要在有控制台且输入未被重定向的情况下使用ReadKey,可以这样: 方法一:使用try-catch(适用于任何.NET版本) ```csharp try { ConsoleKeyInfo key = Console.ReadKey(); } catch (System.InvalidOperationException) { // 处理没有控制台或输入重定向的情况 // 例如:使用其他方式读取或给出提示 } ``` 方法二:条件判断(.NET Framework 4.5+) ```csharp if (!Console.IsInputRedirected && !Console.IsOutputRedirected) { // 通常,如果输入输出都没有被重定向,那么可能有一个控制台 // 但是注意:即使没有被重定向,如果没有控制台(比如GUI应用启动时)也会抛出异常 // 所以这个方法并不完全可靠 Console.ReadKey(); } else { // 使用其他方法 } ``` 注意:在GUI应用程序中,如果你确实需要控制台,可以在启动时分配一个控制台(通过调用AllocConsole)。但这通常不是推荐的做法。 方法三:对于GUI应用程序,如果你希望有控制台窗口,可以使用以下代码: ```csharp [DllImport("kernel32.dll")] static extern bool AllocConsole(); // 在需要的时候调用AllocConsole AllocConsole(); ``` 但请注意,一旦分配了控制台,应用程序将同时具有GUI和控制台窗口。 方法四:避免在可能没有控制台或重定向输入的情况下使用ReadKey。考虑使用配置文件、命令行参数或其他输入方式。 根据引用[4]中提到的,有时在.NET Compact Framework中会遇到其他问题,但这里我们的问题与控制台相关。 总结: 这个异常的原因是无法读取键盘输入,因为控制台不存在或输入被重定向。解决方法包括: - 确保运行环境是控制台应用程序且输入未被重定向。 - 使用try-catch捕获异常并采取替代方案。 - 对于GUI应用程序,如果需要控制台,可以调用AllocConsole(但不推荐,除非必要)。 - 使用Console.Read或Console.ReadLine代替(注意它们的行为不同,ReadLine会等待回车,而ReadKey是读取单个按键并立即响应)。 具体到代码中,如果只是需要暂停程序(例如在调试时防止窗口关闭),可以考虑在控制台应用程序的最后使用Console.ReadLine(),而在GUI应用程序中则不需要。 示例:替代ReadKey的暂停功能 ```csharp // 在控制台应用程序的末尾,为了暂停 Console.WriteLine("按回车键退出..."); Console.ReadLine(); ``` 但是,如果你需要读取单个按键(如选择菜单),那么在没有控制台的情况下,你可能需要改变设计,比如使用命令行参数或配置文件。 针对问题:如果你的应用程序有时作为控制台程序运行,有时作为后台服务(没有控制台),那么你应该避免使用ReadKey,或者根据环境进行分支处理。 因此,我们给出以下步骤: 步骤1:确定你的应用程序类型。 如果是控制台应用程序,并且你希望用户通过键盘交互,那么确保它在控制台窗口运行(而不是通过重定向输入)。 步骤2:修改代码,避免在输入重定向或没有控制台的情况下使用ReadKey。 使用条件判断或异常处理。 步骤3:测试你的应用程序在控制台运行、重定向输入(如:myapp.exe < input.txt)以及作为服务运行(没有控制台)的情况。 代码示例(结合条件判断和异常处理): ```csharp public static ConsoleKeyInfo? SafeReadKey() { // 尝试在支持的环境下读取按键 try { // 在.NET 4.5及以上,我们可以检查重定向 if (Console.IsInputRedirected || Console.IsOutputRedirected) { return null; // 或者使用其他方式 } // 尝试读取按键 return Console.ReadKey(intercept: true); // intercept设为true,按键不显示 } catch (InvalidOperationException) { // 没有控制台的情况 return null; } } ``` 然后,在调用SafeReadKey时,检查返回值是否为null,如果是,则说明无法读取按键,需要采取其他交互方式。 注意:Console.IsInputRedirected和Console.IsOutputRedirected在.NET Framework 4.5及以上版本才可用。如果目标框架低于4.5,则只能通过捕获异常来处理。 因此,根据你的目标框架,选择合适的方案。 引用[2]提到使用ReadLine是可行的,所以如果你可以接受输入一行(按回车结束),那么改用ReadLine是一个简单的解决方案。 结论: 1. 使用Console.Read或Console.ReadLine替代Console.ReadKey,如果这种改变能满足你的需求[^2]。 2. 如果必须使用ReadKey(需要即时响应按键),则添加异常处理或环境检查[^1][^2][^3]。 最后,请注意引用[4]虽然提供了一些其他异常的处理,但与我们当前的问题无关,这里仅供参考。 相关问题: 用户可能还会遇到其他与控制台输入输出相关的问题,因此我们生成以下相关问题: §§相关问题§§ 1. 如何在Windows窗体应用程序中模拟控制台输入? 2. 如何检测应用程序是否拥有控制台窗口? 3..NET Core中处理控制台输入重定向的方法有什么不同? 4. 如何为GUI应用程序分配控制台窗口? </think>### 解决 `System.InvalidOperationException: Cannot read keys...` 异常的方法指南 该异常通常在两种场景下发生: 1. **无控制台环境**:GUI应用/WinService等无控制台窗口时调用 `Console.ReadKey()` 2. **输入流重定向**:使用管道(`|`)或文件(`<`)重定向标准输入流时 #### 解决方案(按优先级排序) ##### 方案1:替换为安全的输入方法 ✅ ```csharp // 替换 ReadKey() var input = Console.ReadLine(); // 安全读取行[^2] // 替换 ReadKey(true) 的无回显场景 var key = (char)Console.Read(); // 安全读取单个字符 ``` ##### 方案2:环境检测 + 异常处理 ```csharp try { if (Environment.UserInteractive) // 检测交互模式[^1] { Console.ReadKey(); } } catch (InvalidOperationException ex) when (ex.Message.Contains("redirected")) { // 处理重定向场景 var input = Console.In.ReadToEnd(); // 读取重定向输入 } ``` ##### 方案3:强制控制台分配(仅Windows GUI应用) ```csharp [DllImport("kernel32.dll")] static extern bool AllocConsole(); // 显式分配控制台 // 在Main方法开头调用 AllocConsole(); // GUI应用添加控制台 Console.ReadKey(); // 现在可安全使用 ``` #### 关键注意事项 1. **输入流检测**:重定向后 `Console.IsInputRedirected` 返回 `true`(.NET 4.5+) 2. **服务应用处理**:Windows服务需在安装时设置允许交互 ```powershell sc config MyService type= own type= interact # 允许服务交互 ``` 3. **跨平台问题**:Linux/macOS服务需使用 `systemd` 配置 `StandardInput=tty` #### 替代方案推荐 💡 ```mermaid graph LR A[需要用户输入] --> B{需即时响应按键?} B -->|是| C[使用窗体应用/WPF] B -->|否| D[改用命令行参数] D --> E[Console.ReadLine读取] ``` > **最佳实践**:在服务/后台程序中完全避免控制台交互,改用日志文件或IPC通信[^4]。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值