Console.Write()将在WPF中挂起,但在Console应用程序中工作

Beg*_*ner 11 c# wpf winapi console-application

请阅读Scott Chamberlain的答案,了解为什么它与WI​​NAPI有关.

在Visual Studio中创建一个新的WPF应用程序并更改代码,MainWindow.xaml.cs如下所示.运行该应用程序.代码将在第二次调用时挂起Console.Write().

MainWindow.xaml.cs

using System;
using System.Text;
using System.Windows;

namespace TestWpf
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            byte[] msg = new byte[1024];

            string msgStr = Encoding.Default.GetString(msg);

            for (int i = 0; i < 10; i++)
            {
                Console.Write(msgStr);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,在Visual Studio中创建一个新的控制台应用程序,Program.cs并按如下所示更改代码.运行该应用程序.它将成功运行,即它不会挂起.

Program.cs中

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            byte[] msg = new byte[1024];

            string msgStr = Encoding.Default.GetString(msg);

            for (int i = 0; i < 100; i++)
            {
                Console.Write(msgStr);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

问题:

  1. 为什么第二次调用Console.Write()会在WPF应用程序中挂起?
  2. 为什么控制台应用程序中的行为不同?
  3. 为什么只有在字符串是一串字符串时才会发生\0?(如果你做1024个空格,它可以正常工作.)

Sco*_*ain 5

基本说明:它挂起,因为在Console.Write显示文本之前缓冲区写入正在变满,并且在传递空字符(\0)时由于我所知的原因而没有耗尽WPF应用程序.


详细说明:当您调用Console.Write它时会创建一个Handle来将其数据输出到WriteFile该句柄并最终调用该句柄.句柄的另一端需要处理写入它的数据,然后将控制权返回给调用者.WPF和我可以找到的控制台应用程序有两个主要区别:

首先,如果您使用控制台应用程序检查句柄类型,您将获得一个类型的句柄FILE_TYPE_CHAR,来自WPF FILE_TYPE_PIPE.

Console.Write(msgStr);

var cOut = Console.OpenStandardOutput();
var handle = cOut.GetType().GetField("_handle", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(cOut);
var method = Type.GetType("Microsoft.Win32.Win32Native").GetMethod("GetFileType", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
var type = method.Invoke(null, new object[] { handle });
Debugger.Break();
Run Code Online (Sandbox Code Playgroud)

其次,如何在接收端处理句柄是不同的.在控制台应用程序中,通过conhost.exe在WPF中读取句柄,由visual studio读入.

挂起本身来自这样一个事实,即缓冲区中的空间有限,只有在句柄必须阻止新的传入请求之前才能将这么多文本排队,因此现有信息可能会耗尽.似乎控制台应用程序的句柄可以处理大量\0字符,但WPF生成的句柄不能.如果差异来自于它是一种不同类型的手柄,或者来自手柄另一侧的处理器读取数据的方式不同,我不知道.

希望通过Windows API调用获得更多经验的人WriteFile可以解释两种句柄类型之间的差异,如果这是因为句柄类型或接收程序,我们会更好地了解这一点.