/** This is a demo that shows how we can have a stream-oriented view of characters from the console* while also listening to console events like mouse, menu, focus, buffer/viewport(1) resize events.** This has always been tricky to do because ReadConsoleW/A doesn't allow retrieving events.* Only ReadConsoleInputW/A returns events, but isn't stream-oriented. Using both doesn't work because* ReadConsoleW/A flushes the input queue, meaning calls to ReadConsoleInputW/A will wait forever.** I do this by deriving a new Stream class which wraps ReadConsoleInputW and accepts a provider/consumer* implementation of BlockingCollection<Kernel32.INPUT_RECORD>. This allows asynchronous monitoring of* console events while simultaneously streaming the character input. I also use Mark Gravell's great* System.IO.Pipelines utility classes (2) and David Hall's excellent P/Invoke wrappers (3) to make this* demo cleaner to read; both are pulled from NuGet.** (1) in versions of windows 10 prior to 1809, the buffer resize event only fires for enlarging* the viewport, as this would cause the buffer to be enlarged too. Now it fires even when* shrinking the viewport, which won't change the buffer size.** (2) https://github.com/mgravell/Pipelines.Sockets.Unofficial* https://www.nuget.org/packages/Pipelines.Sockets.Unofficial** (3) https://github.com/dahall/Vanara* https://www.nuget.org/packages/Vanara.Pinvoke.Kernel32** Oisin Grehan - 2019年4月21日** https://twitter.com/oising* https://github.com/oising*/using System;using System.Collections.Concurrent;using System.IO;using System.Runtime.InteropServices;using System.Text;using System.Threading;using System.Threading.Tasks;using Pipelines.Sockets.Unofficial;using Vanara.PInvoke;namespace Samples.Terminal{internal class Program{private static async Task Main(string[] args){// run for 90 secondsvar timeout = TimeSpan.FromSeconds(90);// in reality this will likely never be reached, but it is useful to guard against// conditions where the queue isn't drained, or not drained fast enough.const int maxNonKeyEventRetention = 128;var source = new CancellationTokenSource(timeout);var token = source.Token;var handle = Kernel32.GetStdHandle(Kernel32.StdHandleType.STD_INPUT_HANDLE);if (!Kernel32.GetConsoleMode(handle, out Kernel32.CONSOLE_INPUT_MODE mode))throw NativeMethods.GetExceptionForWin32Error(Marshal.GetLastWin32Error());mode |= Kernel32.CONSOLE_INPUT_MODE.ENABLE_WINDOW_INPUT;mode |= Kernel32.CONSOLE_INPUT_MODE.ENABLE_VIRTUAL_TERMINAL_INPUT;mode &= ~Kernel32.CONSOLE_INPUT_MODE.ENABLE_ECHO_INPUT;mode &= ~Kernel32.CONSOLE_INPUT_MODE.ENABLE_LINE_INPUT;if (!Kernel32.SetConsoleMode(handle, mode))throw NativeMethods.GetExceptionForLastWin32Error();// base our provider/consumer on a bounded queue to keep memory usage under controlvar events = new BlockingCollection<Kernel32.INPUT_RECORD>(new ConcurrentBoundedQueue<Kernel32.INPUT_RECORD>(maxNonKeyEventRetention));// Task that will consume non-key events asynchronouslyvar consumeEvents = Task.Run(() =>{Console.WriteLine("consumeEvents started");try{while (!events.IsCompleted){// blocking callvar record = events.Take(token);Console.WriteLine("record: {0}",Enum.GetName(typeof(Kernel32.EVENT_TYPE), record.EventType));}}catch (OperationCanceledException){// timeout}Console.WriteLine("consumeEvents ended");}, token);// Task that will watch for key events while feeding non-key events into our provider/consumer collectionvar readInputAndProduceEvents = Task.Run(async () =>{//So, this is the key point - we cannot use the following or we lose all non-key events:// Stream stdin = Console.OpenStandardInput();// get a unicode character stream over console inputStream stdin = new ReadConsoleInputStream(handle, events);// wrap in a System.IO.Pipelines.PipeReader to get clean async and span/memory usagevar reader = StreamConnection.GetReader(stdin);while (!token.IsCancellationRequested){// blocking callvar result = await reader.ReadAsync(token);if (result.IsCanceled)break;var sequence = result.Buffer;var segment = sequence.Start;while (sequence.TryGet(ref segment, out var mem)){// decode back from unicodevar datum = Encoding.Unicode.GetString(mem.Span);Console.Write(datum);}reader.AdvanceTo(sequence.End);}}, token);Console.WriteLine("Running");try{await Task.WhenAll(consumeEvents, readInputAndProduceEvents);}catch (OperationCanceledException){// timeout}Console.WriteLine("press any key...");Console.ReadKey(true);}}}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。