C# Write()将挂起在WPF中,但在控制台应用程序中工作
请阅读Scott Chamberlain的答案,了解为什么它与WINAPI有关 在Visual Studio中创建一个新的WPF应用程序,并在C# Write()将挂起在WPF中,但在控制台应用程序中工作,c#,wpf,winapi,console-application,C#,Wpf,Winapi,Console Application,请阅读Scott Chamberlain的答案,了解为什么它与WINAPI有关 在Visual Studio中创建一个新的WPF应用程序,并在MainWindow.xaml.cs中更改代码,如下所示。运行应用程序。第二次调用Console.Write()时,代码将挂起 MainWindow.xaml.cs using System; using System.Text; using System.Windows; namespace TestWpf { /// <summary&
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);
}
}
}
}
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);
}
}
}
}
问题:
\0
的字符串时才会发生这种情况?(如果使用1024个空格,效果很好。)基本解释:它挂起是因为缓冲区
控制台。写入在文本显示之前写入的内容已满,并且在传入空字符(\0
)时,WPF应用程序不会耗尽,原因我不知道
详细说明:当您调用控制台时。Write
将创建一个输出其数据的句柄,并最终调用该句柄。句柄的另一端需要处理写入它的数据,然后将控制权返回给调用方。WPF和控制台应用程序之间有两个主要区别:
首先,如果您使用控制台应用程序,您将获得类型为FILE\u-type\u-CHAR
的句柄,从WPF您将获得FILE\u-type\u-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();
第二,句柄在接收端的处理方式不同。在控制台应用程序中,句柄由读取,在WPF中,句柄由visual studio读取
挂起本身是因为缓冲区中的空间有限,在句柄必须阻止新的传入请求之前,只能排队等待如此多的文本,这样现有的信息就可以耗尽。控制台应用程序的句柄似乎可以处理大量\0
字符,但WPF生成的句柄不能。如果这个区别是因为它是一个不同类型的句柄,或者是因为句柄另一侧的处理器以不同的方式读取数据,我不知道
希望有人在Windows API调用WriteFile
方面比我更有经验,可以解释这两种句柄类型之间的差异,并能让我们更好地了解这是因为句柄类型还是因为接收程序。因为WPF对控制台窗口没有未更改的句柄。我无法看到Write方法的实现,但您可以在静态控制台类的大多数公共属性上看到一个IO错误,返回“Message=”句柄无效。\r\n“
如果要显示控制台窗口,则在WPF应用程序中,需要执行kernel32.dll非托管库中的代码
请参阅这在我的机器上运行时没有问题(Winodws 8.1 VS2013)。问题与控制台应用程序无关。正如Scott解释的,存在一些阻塞。
它在不在调试器下运行时工作
- 作为WPF应用程序
- As控制台应用程序
当您尝试调试应用程序时,它将挂起。更深层次的原因是您正在通过管道发送\0。管道有一个发送缓冲区(约4 KB)在它阻止进一步写入之前。这是您在kernel32.dll中看到的挂起的WriteFile调用。要能够阻止,必须有人想从您的管道中读取。在这种情况下,这是VS尝试将您的stdout发送到调试器输出窗口。当没有人在侦听时,管道充当空设备,永远不会阻止。
现在回到问题上来,为什么它可以处理除\0之外的所有字符串?这与如何停止从管道读取有关。当接收器接收到\0作为唯一消息时,它可以停止从管道读取。这是进程已退出的信号,不会向其写入进一步的数据。使用\0消息,您违反了该implicit合同并通过管道发送更多数据,但您的客户(VS)已停止侦听您的内容。
这实际上不是一个API,但似乎是一个常见协议。在.NET中,yout get for async pipe读取的最后一条消息为null。其他应用程序(例如VS)似乎以类似的方式处理\0消息,并假设编写器已退出
在这种情况下,关闭管道句柄是合法的,返回false。您也可以向管道写入长度为0字节的消息。或者您可以向管道写入1024 KB的空数组。由消息的读取器决定这是否是停止从管道读取的信号
更新1
因为至少有一个评论者认为逻辑是不够的,所以这里是调试VS的结果。VS确实通过ReadFile从管道中读取
vsdebug!CReader::ReadPipe
检查第一个字节是否为0,这将导致线程终止。如果第一个字节不是0,它将被视为unicode字符串,并复制到调试器输出窗口中显示的字符串缓冲区。您可以通过发送例如1000个c字符(将显示在缓冲区中)来轻松验证这一点。然后,您可以按照与1000 0字节不同的ol流。
事实证明,相关的部分是:
0:048> db ebp-420
1973f794 00 00 00 00 38 63 71 10-fe 03 00 00 92 82 b5 45 ....8cq........E
1973f7a4 63 63 63 63 63 63 63 63-63 63 63 63 63 63 63 63 cccccccccccccccc
1973f7b4 63 63 63 63 63 63 63 63-63 63 63 63 63 63 63 63 cccccccccccccccc
1973f7c4 63 63 63 63 63 63 63 63-63 63 63 63 63 63 63 63 cccccccccccccccc
1973f7d4 63 63 63 63 63 63 63 63-63 63 63 63 63 63 63 63 cccccccccccccccc
1973f7e4 63 63 63 63 63 63 63 63-63 63 63 63 63 63 63 63 cccccccccccccccc
1973f7f4 63 63 63 63 63 63 63 63-63 63 63 63 63 63 63 63 cccccccccccccccc
1973f804 63 63 63 63 63 63 63 63-63 63 63 63 63 63 63 63 cccccccccccccccc
缓冲区包含ebp-410中的数据,其中是我们的c字符。在此之前,存储了缓冲区大小和文件句柄
0:048> u 5667dd48 L50
vsdebug!ReaderThreadStart+0x72:
**5667dd48 80bdf0fbffff00 cmp byte ptr [ebp-410h],0** check if first byte is 0
5667dd4f 7446 je vsdebug!ReaderThreadStart+0xc1 (5667dd97)
5667dd51 899decfbffff mov dword ptr [ebp-414h],ebx
5667dd57 897dfc mov dword ptr [ebp-4],edi
5667dd5a 8d85f0fbffff lea eax,[ebp-410h]
5667dd60 53 push ebx
5667dd61 53 push ebx
5667dd62 50 push eax
5667dd63 8d8decfbffff lea ecx,[ebp-414h]
5667dd69 e8f5960200 call vsdebug!CVSUnicodeString::CopyString (566a7463)
5667dd6e c745fc02000000 mov dword ptr [ebp-4],2
5667dd75 8b4e30 mov ecx,dword ptr [esi+30h]
5667dd78 6aff push 0FFFFFFFFh
5667dd7a ffb5ecfbffff push dword ptr [ebp-414h]
5667dd80 e84453f6ff call vsdebug!CMinimalStreamEx::AddStringW (565e30c9)
5667dd85 834dfcff or dword ptr [ebp-4],0FFFFFFFFh
5667dd89 8d8decfbffff lea ecx,[ebp-414h]
5667dd8f 53 push ebx
5667dd90 e86339f5ff call vsdebug!CVSVoidPointer::Assign (565d16f8)
5667dd95 eb03 jmp vsdebug!ReaderThreadStart+0xc4 (5667dd9a)
** 5667dd97 897e28 mov dword ptr [esi+28h],edi ** When 0 go here and sleep 200ms
5667dd9a 68c8000000 push 0C8h
5667dd9f ff151c228056 call dword ptr [vsdebug!_imp__Sleep (5680221c)]
5667dda5 e978caf9ff jmp vsdebug!ReaderThreadStart+0xd4 (5661a822)
5667ddaa e87ffc0d00 call vsdebug!__report_rangecheckfailure (5675da2e)
5667ddaf cc int 3
5667ddb0 b9fe030000 mov ecx,3FEh
5667ddb5 899de4fbffff mov dword ptr [ebp-41Ch],ebx
5667ddbb 3bc1 cmp eax,ecx
5667ddbd 7702 ja vsdebug!CReader::Stop+0x84 (5667ddc1)
5667ddbf 8bc8 mov ecx,eax
5667ddc1 53 push ebx
5667ddc2 8d85e4fbffff lea eax,[ebp-41Ch]
5667ddc8 50 push eax
5667ddc9 51 push ecx
5667ddca 8d85f0fbffff lea eax,[ebp-410h]
5667ddd0 50 push eax
5667ddd1 ff762c push dword ptr [esi+2Ch]
5667ddd4 ff1590218056 call dword ptr [vsdebug!_imp__ReadFile (56802190)]
5667ddda 85c0 test eax,eax
5667dddc 0f845a80f9ff je vsdebug!CReader::Stop+0x117 (56615e3c)
5667dde2 8b85e4fbffff mov eax,dword ptr [ebp-41Ch]
5667dde8 85c0 test eax,eax
5667ddea 0f844c80f9ff je vsdebug!CReader::Stop+0x117 (56615e3c)
5667ddf0 b900040000 mov ecx,400h
5667ddf5 3bc1 cmp eax,ecx
5667ddf7 736c jae vsdebug!CReader::Stop+0x125 (5667de65)
5667ddf9 889c05f0fbffff mov byte ptr [ebp+eax-410h],bl
5667de00 40 inc eax
5667de01 3bc1 cmp eax,ecx
5667de03 7360 jae vsdebug!CReader::Stop+0x125 (5667de65)
5667de05 889c05f0fbffff mov byte ptr [ebp+eax-410h],bl
5667de0c 389df0fbffff cmp byte ptr [ebp-410h],bl
5667de12 0f842480f9ff je vsdebug!CReader::Stop+0x117 (56615e3c)
5667de18 899decfbffff mov dword ptr [ebp-414h],ebx
5667de1e c745fc01000000 mov dword ptr [ebp-4],1
5667de25 8d85f0fbffff lea eax,[ebp-410h]
5667de2b 53 push ebx
5667de2c 53 push ebx
5667de2d 50 push eax
5667de2e 8d8decfbffff lea ecx,[ebp-414h]
5667de34 e82a960200 call vsdebug!CVSUnicodeString::CopyString (566a7463)
5667de39 c745fc02000000 mov dword ptr [ebp-4],2
5667de40 8b4e30 mov ecx,dword ptr [esi+30h]
5667de43 6aff push 0FFFFFFFFh
5667de45 ffb5ecfbffff push dword ptr [ebp-414h]
5667de4b e87952f6ff call vsdebug!CMinimalStreamEx::AddStringW (565e30c9)
5667de50 834dfcff or dword ptr [ebp-4],0FFFFFFFFh
5667de54 8d8decfbffff lea ecx,[ebp-414h]
5667de5a 53 push ebx
5667de5b e89838f5ff call vsdebug!CVSVoidPointer::Assign (565d16f8)
5667de60 e9d77ff9ff jmp vsdebug!CReader::Stop+0x117 (56615e3c)
5667de65 e8c4fb0d00 call vsdebug!__report_rangecheckfailure (5675da2e)
5667de6a cc int 3
5667de6b b81a7e5d56 mov eax,offset vsdebug!ATL::CAtlMap<unsigned long,CScriptNode *,ATL::CElementTraits<unsigned long>,ATL::CElementTraits<CScriptNode *> >::~CAtlMap<unsigned long,CScriptNode *,ATL::CElementTraits<unsigned long>,ATL::CElementTraits<CScriptNode *> >+0x15 (565d7e1a)
5667de70 c3 ret
5667de71 b84c8e5d56 mov eax,offset vsdebug!ATL::CAtlMap<unsigned long,ATL::CComPtr<IVsHierarchyEvents>,ATL::CElementTraits<unsigned long>,ATL::CElementTraits<ATL::CComPtr<IVsHierarchyEvents> > >::~CAtlMap<unsigned long,ATL::CComPtr<IVsHierarchyEvents>,ATL::CElementTraits<unsigned long>,ATL::CElementTraits<ATL::CComPtr<IVsHierarchyEvents> > >+0x15 (565d8e4c)
5667de76 c3 ret
5667de77 6857000780 push 80070057h
5667de7c e8df0af6ff call vsdebug!treegrid::IGridView::CleanupItems (565de960)
** 0:048> u 5661a822 ** Jump here
vsdebug!ReaderThreadStart+0xd4:
5661a822 395e28 cmp dword ptr [esi+28h],ebx
5661a825 74d0 je vsdebug!ReaderThreadStart+0x26 (5661a7f7)
5661a827 ff7624 push dword ptr [esi+24h]
5661a82a ff157c228056 call dword ptr [vsdebug!_imp__SetEvent (5680227c)]
5661a830 53 push ebx
** 5661a831 ff1508218056 call dword ptr [vsdebug!_imp__ExitThread (56802108)] ** Stop reading
0:048>u 5667dd48 L50
vsdebug!ReaderThreadStart+0x72:
**5667dd48 80bdf0fbffff00 cmp字节ptr[ebp-410h],0**检查第一个字节是否为0
5667dd4f 7446 je vsdebug!ReaderThreadStart+0xc1(5667dd97)
5667dd51 899DECFFFFF mov dword ptr[ebp-414h],ebx
5667dd57 897dfc mov dword ptr[ebp-4],电子数据交换
5667dd5a 8d85f0fbffff lea eax,[ebp-410h]
5667dd60 53推式ebx
5667dd61 53推式ebx
5667dd62 50推式eax
5667dd63 8D8DECFFFFF lea ecx,[ebp-414h]
5667dd69 E8F596200