講解NET CORE 是如何從控制台中讀取輸入流


.NET CORE 怎么樣從控制台中讀取輸入流

從Console.ReadList/Read 的源碼中,可學習到.NET CORE 是怎么樣來讀取輸入流。
也可以學習到是如何使用P/Invoke來調用系統API

Console.ReadList 的源碼為

        [MethodImplAttribute(MethodImplOptions.NoInlining)] public static string ReadLine() { return In.ReadLine(); }

其中In為。


        internal static T EnsureInitialized<T>(ref T field, Func<T> initializer) where T : class => LazyInitializer.EnsureInitialized(ref field, ref InternalSyncObject, initializer); public static TextReader In => EnsureInitialized(ref s_in, () => ConsolePal.GetOrCreateReader());

可以看到他是個TextRead
接下來,我們看看ConsolePal.GetOrCreateReader()方法中,是怎么樣獲取到一個Reader的。
轉到ConsolePal.Windows.cs 的源碼,可以看到,

internal static TextReader GetOrCreateReader() { Stream inputStream = OpenStandardInput(); return SyncTextReader.GetSynchronizedTextReader(inputStream == Stream.Null ? StreamReader.Null : new StreamReader(  stream: inputStream,  encoding: new ConsoleEncoding(Console.InputEncoding),  detectEncodingFromByteOrderMarks: false,  bufferSize: Console.ReadBufferSize,  leaveOpen: true)); }

繼續跳轉,查看方法OpenStandardInput


        public static Stream OpenStandardInput() { return GetStandardFile(Interop.Kernel32.HandleTypes.STD_INPUT_HANDLE, FileAccess.Read); }

繼續看方法


        private static Stream GetStandardFile(int handleType, FileAccess access) { IntPtr handle = Interop.Kernel32.GetStdHandle(handleType); // 此處源碼一坨注釋被我刪掉了。^_^ if (handle == IntPtr.Zero || handle == InvalidHandleValue || (access != FileAccess.Read && !ConsoleHandleIsWritable(handle))) { return Stream.Null; } return new WindowsConsoleStream(handle, access, GetUseFileAPIs(handleType)); }

哈哈,終於要看到了Interop.Kernel32.GetStdHandle 這個方法就是調用系統API接口函數的方法。
Interop.GetStdHandle.cs 中調用GetStdHandle 的系統API
在 System.Console.csproj 的項目文件中。
可以看到,在項目文件中,使用條件編譯,將不同的文件包含進來,調用不同系統的API

<!-- Windows --> <ItemGroup Condition="'$(TargetsWindows)' == 'true'"> <Compile Include="$(CommonPath)\CoreLib\Interop\Windows\Kernel32\Interop.GetStdHandle.cs"> <Link>Common\CoreLib\Interop\Windows\Interop.GetStdHandle.cs</Link> </Compile> </ItemGroup> <!-- Unix --> <ItemGroup Condition=" '$(TargetsUnix)' == 'true'"> </ItemGroup>

回到GetStandardFile 中看到返回一個WindowsConsoleStream
其中Read方法,調用了系統API。


            private static unsafe int ReadFileNative(IntPtr hFile, byte[] bytes, int offset, int count, bool isPipe, out int bytesRead, bool useFileAPIs) { if (bytes.Length - offset < count) throw new IndexOutOfRangeException(SR.IndexOutOfRange_IORaceCondition); // You can't use the fixed statement on an array of length 0. if (bytes.Length == 0) { bytesRead = 0; return Interop.Errors.ERROR_SUCCESS; } bool readSuccess; fixed (byte* p = &bytes[0]) { if (useFileAPIs) { readSuccess = (0 != Interop.Kernel32.ReadFile(hFile, p + offset, count, out bytesRead, IntPtr.Zero)); } else { int charsRead; readSuccess = Interop.Kernel32.ReadConsole(hFile, p + offset, count / BytesPerWChar, out charsRead, IntPtr.Zero); bytesRead = charsRead * BytesPerWChar; } } if (readSuccess) return Interop.Errors.ERROR_SUCCESS; int errorCode = Marshal.GetLastWin32Error(); if (errorCode == Interop.Errors.ERROR_NO_DATA || errorCode == Interop.Errors.ERROR_BROKEN_PIPE) return Interop.Errors.ERROR_SUCCESS; return errorCode; }

useFileAPIs 參數,決定是使用操作系統 ReadFile還是 ReadConsole API。
這2個API。都是可以讀取到控制台的輸入流。


對於.NET CORE 源碼中有很多 XXXX.Unix.cs,XXXX.Windows.cs
類名都是XXXX.例如 ConsolePal 這個內部類。
使用條件條件編譯。達到不同平台使用對應的 OS API來調用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM