• .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)CoreLibInteropWindowsKernel32Interop.GetStdHandle.cs">
    		  <Link>CommonCoreLibInteropWindowsInterop.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来调用。

  • 相关阅读:
    第十九周作业
    第十八周作业
    第十七周作业
    第16周作业
    第15周作业
    第14周作业作业
    第13周作业
    第12周作业
    第二阶段月考
    RDB和AOF的区别
  • 原文地址:https://www.cnblogs.com/ruxia/p/11431799.html
Copyright © 2020-2023  润新知