3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 /*============================================================
8 ** <OWNER>Microsoft</OWNER>
10 ** Class: ConsoleStream
13 ** Purpose: Exposes a separate Stream for Console IO and
14 ** handles WinCE appropriately. Also keeps us from using the
15 ** ThreadPool for all Console output.
18 ===========================================================*/
22 using System.Runtime.InteropServices;
23 using System.Security;
24 using Microsoft.Win32;
25 using Microsoft.Win32.SafeHandles;
26 using System.Runtime.CompilerServices;
27 using System.Runtime.Versioning;
28 using System.Threading;
29 using System.Diagnostics.Contracts;
33 internal sealed class __ConsoleStream : Stream
36 // We know that if we are using console APIs rather than file APIs, then the encoding
37 // is Encoding.Unicode implying 2 bytes per character:
38 const int BytesPerWChar = 2;
40 [System.Security.SecurityCritical] // auto-generated
41 private SafeFileHandle _handle;
42 private bool _canRead;
43 private bool _canWrite;
45 private bool _useFileAPIs;
46 private bool _isPipe; // When reading from pipes, we need to properly handle EOF cases.
48 [System.Security.SecurityCritical] // auto-generated
49 [ResourceExposure(ResourceScope.Process)]
50 internal __ConsoleStream(SafeFileHandle handle, FileAccess access, bool useFileAPIs)
52 Contract.Assert(handle != null && !handle.IsInvalid, "__ConsoleStream expects a valid handle!");
54 _canRead = ( (access & FileAccess.Read) == FileAccess.Read );
55 _canWrite = ( (access & FileAccess.Write) == FileAccess.Write);
56 _useFileAPIs = useFileAPIs;
57 _isPipe = Win32Native.GetFileType(handle) == Win32Native.FILE_TYPE_PIPE;
60 public override bool CanRead {
62 get { return _canRead; }
65 public override bool CanWrite {
67 get { return _canWrite; }
70 public override bool CanSeek {
75 public override long Length {
77 __Error.SeekNotSupported();
78 return 0; // compiler appeasement
82 public override long Position {
84 __Error.SeekNotSupported();
85 return 0; // compiler appeasement
88 __Error.SeekNotSupported();
92 [System.Security.SecuritySafeCritical] // auto-generated
93 protected override void Dispose(bool disposing)
95 // We're probably better off not closing the OS handle here. First,
96 // we allow a program to get multiple instances of __ConsoleStreams
97 // around the same OS handle, so closing one handle would invalidate
98 // them all. Additionally, we want a second AppDomain to be able to
99 // write to stdout if a second AppDomain quits.
100 if (_handle != null) {
105 base.Dispose(disposing);
108 [System.Security.SecuritySafeCritical] // auto-generated
109 public override void Flush()
111 if (_handle == null) __Error.FileNotOpen();
112 if (!CanWrite) __Error.WriteNotSupported();
115 public override void SetLength(long value)
117 __Error.SeekNotSupported();
120 [System.Security.SecuritySafeCritical] // auto-generated
121 [ResourceExposure(ResourceScope.None)]
122 [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
123 public override int Read([In, Out] byte[] buffer, int offset, int count) {
125 throw new ArgumentNullException("buffer");
126 if (offset < 0 || count < 0)
127 throw new ArgumentOutOfRangeException((offset < 0 ? "offset" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
128 if (buffer.Length - offset < count)
129 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
130 Contract.EndContractBlock();
131 if (!_canRead) __Error.ReadNotSupported();
134 int errCode = ReadFileNative(_handle, buffer, offset, count, _useFileAPIs, _isPipe, out bytesRead);
136 if (Win32Native.ERROR_SUCCESS != errCode)
137 __Error.WinIOError(errCode, String.Empty);
142 public override long Seek(long offset, SeekOrigin origin) {
143 __Error.SeekNotSupported();
144 return 0; // compiler appeasement
147 [System.Security.SecuritySafeCritical] // auto-generated
148 [ResourceExposure(ResourceScope.None)]
149 [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
150 public override void Write(byte[] buffer, int offset, int count) {
152 throw new ArgumentNullException("buffer");
153 if (offset < 0 || count < 0)
154 throw new ArgumentOutOfRangeException((offset < 0 ? "offset" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
155 if (buffer.Length - offset < count)
156 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
157 Contract.EndContractBlock();
158 if (!_canWrite) __Error.WriteNotSupported();
160 int errCode = WriteFileNative(_handle, buffer, offset, count, _useFileAPIs);
162 if (Win32Native.ERROR_SUCCESS != errCode)
163 __Error.WinIOError(errCode, String.Empty);
168 // P/Invoke wrappers for writing to and from a file, nearly identical
169 // to the ones on FileStream. These are duplicated to save startup/hello
170 // world working set.
171 [System.Security.SecurityCritical] // auto-generated
172 [ResourceExposure(ResourceScope.Process)]
173 [ResourceConsumption(ResourceScope.Process)]
174 private unsafe static int ReadFileNative(SafeFileHandle hFile, byte[] bytes, int offset, int count, bool useFileAPIs, bool isPipe, out int bytesRead) {
176 Contract.Requires(offset >= 0, "offset >= 0");
177 Contract.Requires(count >= 0, "count >= 0");
178 Contract.Requires(bytes != null, "bytes != null");
179 // Don't corrupt memory when multiple threads are erroneously writing
180 // to this stream simultaneously.
181 if (bytes.Length - offset < count)
182 throw new IndexOutOfRangeException(Environment.GetResourceString("IndexOutOfRange_IORaceCondition"));
183 Contract.EndContractBlock();
185 // You can't use the fixed statement on an array of length 0.
186 if (bytes.Length == 0) {
188 return Win32Native.ERROR_SUCCESS;
191 // First, wait bytes to become available. This is preferable to letting ReadFile block,
192 // since ReadFile is not abortable (via Thread.Abort), while WaitForAvailableConsoleInput is.
194 #if !FEATURE_CORESYSTEM // CoreSystem isn't signaling stdin when input is available so we can't block on it
195 WaitForAvailableConsoleInput(hFile, isPipe);
202 #endif // !FEATURE_PAL
204 fixed (byte* p = bytes) {
205 readSuccess = (0 != Win32Native.ReadFile(hFile, p + offset, count, out bytesRead, IntPtr.Zero));
211 fixed (byte* p = bytes) {
213 readSuccess = Win32Native.ReadConsoleW(hFile, p + offset, count / BytesPerWChar, out charsRead, IntPtr.Zero);
214 bytesRead = charsRead * BytesPerWChar;
218 #endif // !FEATURE_PAL
221 return Win32Native.ERROR_SUCCESS;
223 int errorCode = Marshal.GetLastWin32Error();
225 // For pipes that are closing or broken, just stop.
226 // (E.g. ERROR_NO_DATA ("pipe is being closed") is returned when we write to a console that is closing;
227 // ERROR_BROKEN_PIPE ("pipe was closed") is returned when stdin was closed, which is mot an error, but EOF.)
228 if (errorCode == Win32Native.ERROR_NO_DATA || errorCode == Win32Native.ERROR_BROKEN_PIPE)
229 return Win32Native.ERROR_SUCCESS;
233 [System.Security.SecurityCritical] // auto-generated
234 [ResourceExposure(ResourceScope.Process)]
235 [ResourceConsumption(ResourceScope.Process)]
236 private static unsafe int WriteFileNative(SafeFileHandle hFile, byte[] bytes, int offset, int count, bool useFileAPIs) {
238 Contract.Requires(offset >= 0, "offset >= 0");
239 Contract.Requires(count >= 0, "count >= 0");
240 Contract.Requires(bytes != null, "bytes != null");
241 Contract.Requires(bytes.Length >= offset + count, "bytes.Length >= offset + count");
243 // You can't use the fixed statement on an array of length 0.
244 if (bytes.Length == 0)
245 return Win32Native.ERROR_SUCCESS;
251 #endif // !FEATURE_PAL
253 fixed (byte* p = bytes) {
255 writeSuccess = (0 != Win32Native.WriteFile(hFile, p + offset, count, out numBytesWritten, IntPtr.Zero));
256 Contract.Assert(!writeSuccess || count == numBytesWritten);
262 // Note that WriteConsoleW has a max limit on num of chars to write (64K)
263 // [http://msdn.microsoft.com/en-us/library/ms687401.aspx]
264 // However, we do not need to worry about that becasue the StreamWriter in Console has
265 // a much shorter buffer size anyway.
266 fixed (byte* p = bytes) {
268 writeSuccess = Win32Native.WriteConsoleW(hFile, p + offset, count / BytesPerWChar, out charsWritten, IntPtr.Zero);
269 Contract.Assert(!writeSuccess || count / BytesPerWChar == charsWritten);
272 #endif // !FEATURE_PAL
275 return Win32Native.ERROR_SUCCESS;
277 int errorCode = Marshal.GetLastWin32Error();
279 // For pipes that are closing or broken, just stop.
280 // (E.g. ERROR_NO_DATA ("pipe is being closed") is returned when we write to a console that is closing;
281 // ERROR_BROKEN_PIPE ("pipe was closed") is returned when stdin was closed, which is mot an error, but EOF.)
282 if (errorCode == Win32Native.ERROR_NO_DATA || errorCode == Win32Native.ERROR_BROKEN_PIPE)
283 return Win32Native.ERROR_SUCCESS;
287 [System.Security.SecurityCritical] // auto-generated
288 [MethodImplAttribute(MethodImplOptions.InternalCall)]
289 private static extern void WaitForAvailableConsoleInput(SafeFileHandle file, bool isPipe);