【问题标题】:Exception when trying to call Console.Clear() after opening a console a second time再次打开控制台后尝试调用 Console.Clear() 时出现异常
【发布时间】:2018-05-13 05:09:56
【问题描述】:

我正在尝试通过单击按钮从 winform 应用程序打开控制台。我正在通过下面的代码执行此操作。

[DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool AllocConsole();

    [DllImport("kernel32.dll")]
    static extern Boolean FreeConsole();

private void button1_Click(object sender, EventArgs e)
    {
        int userInput = 0;
        AllocConsole();

        do
        {
            Console.Clear();
            Console.WriteLine("Hello World");
            Console.WriteLine("Select 1 or 2");
            int.TryParse(Console.ReadLine(), out userInput);
        } while (userInput != 1 && userInput != 2);

        FreeConsole();
    }

第一次打开控制台时,它工作正常。尝试第二次打开控制台可以正常工作,但是一旦调用 Console.Clear();,我就会得到:

在 mscorlib.dll 中发生了“System.IO.IOException”类型的未处理异常

附加信息:句柄无效。

Console.WriteLine();Console.ReadLine(); 将引发相同的异常。 我已经尝试了thisthisthis 提出的解决方案,但我最终得到了相同的“句柄无效”。 Console.Clear(); 出错。

我尝试过的一些额外代码:

    [DllImport("kernel32.dll",
        EntryPoint = "GetStdHandle",
        SetLastError = true,
        CharSet = CharSet.Auto,
        CallingConvention = CallingConvention.StdCall)]
    private static extern IntPtr GetStdHandle(int nStdHandle);

    [DllImport("kernel32.dll",
        EntryPoint = "AllocConsole",
        SetLastError = true,
        CharSet = CharSet.Auto,
        CallingConvention = CallingConvention.StdCall)]
    private static extern int AllocConsole();

    [DllImport("kernel32.dll")]
    static extern Boolean FreeConsole();


    private const int STD_OUTPUT_HANDLE = -11;
    private const int MY_CODE_PAGE = 437;

private void button1_Click(object sender, EventArgs e)
    {
        int userInput = 0;
        AllocConsole();

        IntPtr stdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
        SafeFileHandle safeFileHandle = new SafeFileHandle(stdHandle, true);
        FileStream fileStream = new FileStream(safeFileHandle, FileAccess.Write);
        Encoding encoding = System.Text.Encoding.GetEncoding(MY_CODE_PAGE);
        StreamWriter standardOutput = new StreamWriter(fileStream, encoding);
        standardOutput.AutoFlush = true;
        Console.SetOut(standardOutput);
        Stream streamIn = Console.OpenStandardInput();
        TextReader readerIn = new StreamReader(streamIn);
        Console.SetIn(readerIn);

        do
        {
            Console.Clear();
            Console.WriteLine("Hello World");
            Console.WriteLine("Select 1 or 2");
            int.TryParse(Console.ReadLine(), out userInput);
        } while (userInput != 1 && userInput != 2);

        FreeConsole();
    }

这对于Console.WriteLine();Console.ReadLine(); 来说似乎没问题,但我仍然无法绕过Console.Clear(); 引发的异常。如果我遗漏了什么,谁能告诉我?

【问题讨论】:

  • 一个进程只能关联一个控制台docs.microsoft.com/en-us/windows/console/allocconsole
  • @Tony "一个进程可以使用 FreeConsole 函数将自己与当前控制台分离,然后它可以调用 AllocConsole 创建一个新控制台或 AttachConsole 连接到另一个控制台。"从您的来源...
  • 你没有检查 AllocConsole() 是否成功
  • @BugFinder,怎么会不成功?我能够很好地通过 AllocConsole() 并执行 Console.WriteLine 和 Console.ReadLine。两者都很好,特别是 Console.Clear() 是引发异常的原因。尽管如此,我只是进行了一次测试,并且可以确认它是成功的。 AllocConsole() 返回 1。
  • 您是在 Visual Studio 中运行的吗?

标签: c#


【解决方案1】:

我怀疑是否可以多次附加/分离到控制台。据我了解the code of private static IntPtr ConsoleInputHandleConsoleOutputHandle 也是如此),句柄在首次使用时初始化一次,因此当您从控制台分离进程并再次重新附加时,句柄无效。

所以恕我直言,根据需要使用 Console 类是不可行的(我也不知道任何同时作为控制台和 Win32 应用程序工作的程序的实时示例 - 我见过的大多数应用程序都提供两个版本的 .exe) .

如果你真的需要 Windows 控制台,我想你可以尝试在 Windows API 之上提供你自己的控制台包装器。如果您只需要控制台的外观,您可以渲染您自己的“类似控制台”的窗口。

【讨论】:

  • 如果是这种情况,我预计 AllocConsole 会失败。相反,它成功了,我可以使用 Console.WriteLine 和 Console.ReadLine。如果我可以使用这两种方法,为什么不能使用 Console.Clear()?它是否以某种方式使用了与前两种方法使用的句柄不同的句柄?
  • @Sudsy1002 as far as I see WriteLine 无条件使用句柄(这对我来说很奇怪,似乎不安全)。 Clear 方法检查句柄是否有效(参见上面的链接),并且可能这个检查是抛出的。我不知道为什么它是这样编码的。
猜你喜欢
  • 2021-09-24
  • 1970-01-01
  • 1970-01-01
  • 2023-04-01
  • 2023-01-27
  • 2020-11-13
  • 2012-09-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多