Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / mscorlib / system / io / __consolestream.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 /*============================================================
7 **
8 ** <OWNER>Microsoft</OWNER>
9 ** 
10 ** Class:  ConsoleStream
11 **
12 **
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.
16 **
17 **
18 ===========================================================*/
19
20 using System;
21 using System.Text;
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;
30
31 namespace System.IO {
32
33     internal sealed class __ConsoleStream : Stream
34     {
35
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;
39
40         [System.Security.SecurityCritical] // auto-generated
41         private SafeFileHandle _handle;
42         private bool _canRead;
43         private bool _canWrite;
44
45         private bool _useFileAPIs;
46         private bool _isPipe;  // When reading from pipes, we need to properly handle EOF cases.
47
48         [System.Security.SecurityCritical]  // auto-generated
49         [ResourceExposure(ResourceScope.Process)]
50         internal __ConsoleStream(SafeFileHandle handle, FileAccess access, bool useFileAPIs)
51         {
52             Contract.Assert(handle != null && !handle.IsInvalid, "__ConsoleStream expects a valid handle!");
53             _handle = 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;
58         }
59     
60         public override bool CanRead {
61             [Pure]
62             get { return _canRead; }
63         }
64
65         public override bool CanWrite {
66             [Pure]
67             get { return _canWrite; }
68         }
69
70         public override bool CanSeek {
71             [Pure]
72             get { return false; }
73         }
74
75         public override long Length {
76             get {
77                 __Error.SeekNotSupported();
78                 return 0; // compiler appeasement
79             }
80         }
81
82         public override long Position {
83             get { 
84                 __Error.SeekNotSupported();
85                 return 0; // compiler appeasement
86             }
87             set {
88                 __Error.SeekNotSupported();
89             }
90         }
91
92         [System.Security.SecuritySafeCritical]  // auto-generated
93         protected override void Dispose(bool disposing)
94         {
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) {
101                 _handle = null;
102             }
103             _canRead = false;
104             _canWrite = false;
105             base.Dispose(disposing);
106         }
107
108         [System.Security.SecuritySafeCritical]  // auto-generated
109         public override void Flush()
110         {
111             if (_handle == null) __Error.FileNotOpen();
112             if (!CanWrite) __Error.WriteNotSupported();
113         }
114
115         public override void SetLength(long value)
116         {
117             __Error.SeekNotSupported();
118         }
119
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) {
124             if (buffer==null)
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();
132
133             int bytesRead;
134             int errCode = ReadFileNative(_handle, buffer, offset, count, _useFileAPIs, _isPipe, out bytesRead);
135
136             if (Win32Native.ERROR_SUCCESS != errCode)
137                 __Error.WinIOError(errCode, String.Empty);
138
139             return bytesRead;
140         }
141
142         public override long Seek(long offset, SeekOrigin origin) {
143             __Error.SeekNotSupported();
144             return 0; // compiler appeasement
145         }
146
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) {
151             if (buffer==null)
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();
159
160             int errCode = WriteFileNative(_handle, buffer, offset, count, _useFileAPIs);
161
162             if (Win32Native.ERROR_SUCCESS != errCode)                 
163                 __Error.WinIOError(errCode, String.Empty);
164
165             return;
166         }
167
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) {
175
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();
184
185             // You can't use the fixed statement on an array of length 0.
186             if (bytes.Length == 0) {
187                 bytesRead = 0;
188                 return Win32Native.ERROR_SUCCESS;
189             }
190
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.
193
194 #if !FEATURE_CORESYSTEM // CoreSystem isn't signaling stdin when input is available so we can't block on it
195             WaitForAvailableConsoleInput(hFile, isPipe);
196 #endif
197
198             bool readSuccess;
199
200 #if !FEATURE_PAL
201             if (useFileAPIs) {
202 #endif  // !FEATURE_PAL
203
204                 fixed (byte* p = bytes) {
205                     readSuccess = (0 != Win32Native.ReadFile(hFile, p + offset, count, out bytesRead, IntPtr.Zero));
206                 }
207
208 #if !FEATURE_PAL
209             } else {
210
211                 fixed (byte* p = bytes) {
212                     int charsRead;
213                     readSuccess = Win32Native.ReadConsoleW(hFile, p + offset, count / BytesPerWChar, out charsRead, IntPtr.Zero);
214                     bytesRead = charsRead * BytesPerWChar;
215                 }
216
217             }
218 #endif  // !FEATURE_PAL
219
220             if (readSuccess)
221                 return Win32Native.ERROR_SUCCESS;
222
223             int errorCode = Marshal.GetLastWin32Error();
224
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;
230             return errorCode;
231         }
232
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) {
237
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");
242
243             // You can't use the fixed statement on an array of length 0.
244             if (bytes.Length == 0)             
245                 return Win32Native.ERROR_SUCCESS;
246           
247             bool writeSuccess;
248
249 #if !FEATURE_PAL
250             if (useFileAPIs) {
251 #endif  // !FEATURE_PAL
252
253                 fixed (byte* p = bytes) {
254                     int numBytesWritten;
255                     writeSuccess = (0 != Win32Native.WriteFile(hFile, p + offset, count, out numBytesWritten, IntPtr.Zero));
256                     Contract.Assert(!writeSuccess || count == numBytesWritten);
257                 }
258
259 #if !FEATURE_PAL
260             } else {
261
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) {
267                     Int32 charsWritten;
268                     writeSuccess = Win32Native.WriteConsoleW(hFile, p + offset, count / BytesPerWChar, out charsWritten, IntPtr.Zero);
269                     Contract.Assert(!writeSuccess || count / BytesPerWChar == charsWritten);
270                 }
271             }
272 #endif  // !FEATURE_PAL
273
274             if (writeSuccess)
275                 return Win32Native.ERROR_SUCCESS;
276
277             int errorCode = Marshal.GetLastWin32Error();
278
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;
284             return errorCode;            
285         }
286
287         [System.Security.SecurityCritical]  // auto-generated
288         [MethodImplAttribute(MethodImplOptions.InternalCall)]
289         private static extern void WaitForAvailableConsoleInput(SafeFileHandle file, bool isPipe);
290     }
291 }