Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System / sys / system / IO / ports / SerialStream.cs
1 // ==++==
2 //
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 //
5 // ==--==
6 /*=============================================================================
7 **
8 ** Class: SerialStream
9 **
10 ** Purpose: Class for enabling low-level sync and async control over a serial
11 **        : communications resource.
12 **
13 ** Date: August, 2002
14 **
15 =============================================================================*/
16
17 using System;
18 using System.Collections;
19 using System.ComponentModel;
20 using System.Diagnostics;
21 using System.Diagnostics.CodeAnalysis;
22 using System.Diagnostics.Contracts;
23 using System.Globalization;
24 using System.IO;
25 using System.Resources;
26 using System.Runtime;
27 using System.Runtime.CompilerServices;
28 using System.Runtime.InteropServices;
29 using System.Runtime.Remoting.Messaging;
30 using System.Runtime.Versioning;
31 using System.Security;
32 using System.Security.Permissions;
33 using System.Text;
34 using System.Threading;
35
36 using Microsoft.Win32;
37 using Microsoft.Win32.SafeHandles;
38
39
40 // Notes about the SerialStream:
41 //  * The stream is always opened via the SerialStream constructor.
42 //  * Lifetime of the COM port's handle is controlled via a SafeHandle.  Thus, all properties are available
43 //  * only when the SerialStream is open and not disposed.
44 //  * Handles to serial communications resources here always:
45 //  * 1) own the handle
46 //  * 2) are opened for asynchronous operation
47 //  * 3) set access at the level of FileAccess.ReadWrite
48 //  * 4) Allow for reading AND writing
49 //  * 5) Disallow seeking, since they encapsulate a file of type FILE_TYPE_CHAR
50
51 namespace System.IO.Ports
52 {
53     internal sealed class SerialStream : Stream
54     {
55         const int errorEvents = (int) (SerialError.Frame | SerialError.Overrun |
56                                  SerialError.RXOver | SerialError.RXParity | SerialError.TXFull);
57         const int receivedEvents = (int) (SerialData.Chars | SerialData.Eof);
58         const int pinChangedEvents = (int) (SerialPinChange.Break | SerialPinChange.CDChanged | SerialPinChange.CtsChanged |
59                                       SerialPinChange.Ring | SerialPinChange.DsrChanged);
60
61         const int infiniteTimeoutConst = -2;
62
63         // members supporting properties exposed to SerialPort
64         private string portName;
65         private byte parityReplace = (byte) '?';
66         private bool inBreak = false;               // port is initially in non-break state
67         private bool isAsync = true;
68         private Handshake handshake;
69         private bool rtsEnable = false;
70
71         // The internal C# representations of Win32 structures necessary for communication
72         // hold most of the internal "fields" maintaining information about the port.
73         private UnsafeNativeMethods.DCB dcb;
74         private UnsafeNativeMethods.COMMTIMEOUTS commTimeouts;
75         private UnsafeNativeMethods.COMSTAT comStat;
76         private UnsafeNativeMethods.COMMPROP commProp;
77
78         // internal-use members
79         // private const long dsrTimeout = 0L; -- Not used anymore.
80         private const int maxDataBits = 8;
81         private const int minDataBits = 5;
82         internal SafeFileHandle _handle = null;
83         internal EventLoopRunner eventRunner;
84
85         private byte[] tempBuf;                 // used to avoid multiple array allocations in ReadByte()
86
87         // called whenever any async i/o operation completes.
88         private unsafe static readonly IOCompletionCallback IOCallback = new IOCompletionCallback(SerialStream.AsyncFSCallback);
89
90         // three different events, also wrapped by SerialPort.
91         internal event SerialDataReceivedEventHandler   DataReceived;      // called when one character is received.
92         internal event SerialPinChangedEventHandler PinChanged;    // called when any of the pin/ring-related triggers occurs
93         internal event SerialErrorReceivedEventHandler      ErrorReceived;         // called when any runtime error occurs on the port (frame, overrun, parity, etc.)
94
95
96         // ----SECTION: inherited properties from Stream class ------------*
97
98         // These six properites are required for SerialStream to inherit from the abstract Stream class.
99         // Note four of them are always true or false, and two of them throw exceptions, so these
100         // are not usefully queried by applications which know they have a SerialStream, etc...
101         public override bool CanRead
102         {
103             get { return (_handle != null); }
104         }
105
106         public override bool CanSeek
107         {
108             get { return false; }
109         }
110
111         public override bool CanTimeout {
112             get { return (_handle != null); }
113         }
114         
115         public override bool CanWrite
116         {
117             get { return (_handle != null); }
118         }
119
120         public override long Length
121         {
122             get { throw new NotSupportedException(SR.GetString(SR.NotSupported_UnseekableStream)); }
123         }
124
125
126         public override long Position
127         {
128             get { throw new NotSupportedException(SR.GetString(SR.NotSupported_UnseekableStream)); }
129             set { throw new NotSupportedException(SR.GetString(SR.NotSupported_UnseekableStream)); }
130         }
131
132         // ----- new get-set properties -----------------*
133
134         // Standard port properties, also called from SerialPort
135         // BaudRate may not be settable to an arbitrary integer between dwMinBaud and dwMaxBaud,
136         // and is limited only by the serial driver.  Typically about twelve values such
137         // as Winbase.h's CBR_110 through CBR_256000 are used.
138         internal int BaudRate
139         {
140             //get { return (int) dcb.BaudRate; }
141             set
142             {
143                 if (value <= 0 || (value > commProp.dwMaxBaud && commProp.dwMaxBaud > 0))
144                 {
145                     // if no upper bound on baud rate imposed by serial driver, note that argument must be positive
146                     if (commProp.dwMaxBaud == 0)
147                     {
148                         throw new ArgumentOutOfRangeException("baudRate",
149                             SR.GetString(SR.ArgumentOutOfRange_NeedPosNum));
150                     }
151                     else
152                     {
153                         // otherwise, we can present the bounds on the baud rate for this driver
154                         throw new ArgumentOutOfRangeException("baudRate",
155                             SR.GetString(SR.ArgumentOutOfRange_Bounds_Lower_Upper, 0, commProp.dwMaxBaud));
156                     }
157                 }
158                 // Set only if it's different.  Rollback to previous values if setting fails.
159                 //  This pattern occurs through most of the other properties in this class.
160                 if(value != dcb.BaudRate)
161                 {
162                     int baudRateOld = (int) dcb.BaudRate;
163                     dcb.BaudRate = (uint) value;
164
165                     if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false)
166                     {
167                         dcb.BaudRate = (uint) baudRateOld;
168                         InternalResources.WinIOError();
169                     }
170                 }
171             }
172         }
173
174         public bool BreakState {
175             get { return inBreak; }
176             set {
177                 if (value) {
178                     if (UnsafeNativeMethods.SetCommBreak(_handle) == false)
179                         InternalResources.WinIOError();
180                     inBreak = true;
181                 }
182                 else {
183                     if (UnsafeNativeMethods.ClearCommBreak(_handle) == false)
184                         InternalResources.WinIOError();
185                     inBreak = false;
186                 }
187             }
188         }
189
190         internal int DataBits
191         {
192             //get  { return (int) dcb.ByteSize; }
193             set
194             {
195                 Debug.Assert(!(value < minDataBits || value > maxDataBits), "An invalid value was passed to DataBits");
196                 if (value != dcb.ByteSize)
197                 {
198                     byte byteSizeOld = dcb.ByteSize;
199                     dcb.ByteSize = (byte) value;
200
201                     if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false)
202                     {
203                         dcb.ByteSize = byteSizeOld;
204                         InternalResources.WinIOError();
205                     }
206                 }
207             }
208         }
209
210
211         internal bool DiscardNull
212         {
213             //get {   return (GetDcbFlag(NativeMethods.FNULL) == 1);}
214             set
215             {
216                 int fNullFlag = GetDcbFlag(NativeMethods.FNULL);
217                 if(value == true && fNullFlag == 0 || value == false && fNullFlag == 1)
218                 {
219                     int fNullOld = fNullFlag;
220                     SetDcbFlag(NativeMethods.FNULL, value ? 1 : 0);
221
222                     if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false)
223                     {
224                         SetDcbFlag(NativeMethods.FNULL, fNullOld);
225                         InternalResources.WinIOError();
226                     }
227                 }
228             }
229         }
230
231         internal bool DtrEnable
232         {
233             get {   
234                 int fDtrControl = GetDcbFlag(NativeMethods.FDTRCONTROL);
235                 
236                 return (fDtrControl == NativeMethods.DTR_CONTROL_ENABLE);
237             }
238             set
239             {
240                 // first set the FDTRCONTROL field in the DCB struct
241                 int fDtrControlOld = GetDcbFlag(NativeMethods.FDTRCONTROL);
242
243                 SetDcbFlag(NativeMethods.FDTRCONTROL, value ? NativeMethods.DTR_CONTROL_ENABLE : NativeMethods.DTR_CONTROL_DISABLE);
244                 if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false)
245                 {
246                     SetDcbFlag(NativeMethods.FDTRCONTROL, fDtrControlOld);
247                     InternalResources.WinIOError();
248                 }
249
250                 // then set the actual pin 
251                 if (!UnsafeNativeMethods.EscapeCommFunction(_handle, value ? NativeMethods.SETDTR : NativeMethods.CLRDTR))
252                     InternalResources.WinIOError();
253
254             }
255         }
256
257         internal Handshake Handshake
258         {
259             //get  { return handshake; }
260             set
261             {
262
263                 Debug.Assert(!(value < System.IO.Ports.Handshake.None || value > System.IO.Ports.Handshake.RequestToSendXOnXOff), 
264                     "An invalid value was passed to Handshake");
265
266                 if(value != handshake)
267                 {
268                     // in the DCB, handshake affects the fRtsControl, fOutxCtsFlow, and fInX, fOutX fields,
269                     // so we must save everything in that closure before making any changes.
270                     Handshake handshakeOld = handshake;
271                     int fInOutXOld = GetDcbFlag(NativeMethods.FINX);
272                     int fOutxCtsFlowOld = GetDcbFlag(NativeMethods.FOUTXCTSFLOW);
273                     int fRtsControlOld = GetDcbFlag(NativeMethods.FRTSCONTROL);
274
275                     handshake = value;
276                     int fInXOutXFlag = (handshake == Handshake.XOnXOff || handshake == Handshake.RequestToSendXOnXOff) ? 1 : 0;
277                     SetDcbFlag(NativeMethods.FINX, fInXOutXFlag);
278                     SetDcbFlag(NativeMethods.FOUTX, fInXOutXFlag);
279
280                     SetDcbFlag(NativeMethods.FOUTXCTSFLOW, (handshake == Handshake.RequestToSend ||
281                         handshake == Handshake.RequestToSendXOnXOff) ? 1 : 0);
282
283                     if ((handshake == Handshake.RequestToSend ||
284                         handshake == Handshake.RequestToSendXOnXOff))
285                     {
286                         SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_HANDSHAKE);
287                     }
288                     else if (rtsEnable)
289                     {
290                         SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_ENABLE);
291                     }
292                     else {
293                         SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_DISABLE);
294                     }                        
295
296                     if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false)
297                     {
298                         handshake = handshakeOld;
299                         SetDcbFlag(NativeMethods.FINX, fInOutXOld);
300                         SetDcbFlag(NativeMethods.FOUTX, fInOutXOld);
301                         SetDcbFlag(NativeMethods.FOUTXCTSFLOW, fOutxCtsFlowOld);
302                         SetDcbFlag(NativeMethods.FRTSCONTROL, fRtsControlOld);
303                         InternalResources.WinIOError();
304                     }
305
306                 }
307             }
308         }
309
310         internal bool IsOpen {
311             get {
312                 return _handle != null && !eventRunner.ShutdownLoop;
313             }
314         }
315         
316         internal Parity Parity 
317         { 
318             //get     {   return (Parity) dcb.Parity;     } 
319             set 
320             { 
321                 Debug.Assert(!(value < Parity.None || value > Parity.Space), "An invalid value was passed to Parity");
322
323                 if((byte) value != dcb.Parity)
324                 {
325                     byte parityOld = dcb.Parity;
326
327                     // in the DCB structure, the parity setting also potentially effects:
328                     // fParity, fErrorChar, ErrorChar
329                     // so these must be saved as well.
330                     int fParityOld = GetDcbFlag(NativeMethods.FPARITY);
331                     byte ErrorCharOld = dcb.ErrorChar;
332                     int fErrorCharOld = GetDcbFlag(NativeMethods.FERRORCHAR);
333                     dcb.Parity = (byte) value;
334
335                     int parityFlag = (dcb.Parity == (byte) Parity.None) ? 0 : 1;
336                     SetDcbFlag(NativeMethods.FPARITY, parityFlag);
337                     if (parityFlag == 1)
338                     {
339                         SetDcbFlag(NativeMethods.FERRORCHAR, (parityReplace != '\0') ? 1 : 0);
340                         dcb.ErrorChar = parityReplace;
341                     }
342                     else
343                     {
344                         SetDcbFlag(NativeMethods.FERRORCHAR, 0);
345                         dcb.ErrorChar = (byte) '\0';
346                     }
347                     if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false)
348                     {
349                         dcb.Parity = parityOld;
350                         SetDcbFlag(NativeMethods.FPARITY, fParityOld);
351
352                         dcb.ErrorChar = ErrorCharOld;
353                         SetDcbFlag(NativeMethods.FERRORCHAR, fErrorCharOld);
354
355                         InternalResources.WinIOError();
356                     }
357                 }
358             }
359         }
360
361         // ParityReplace is the eight-bit character which replaces any bytes which
362         // ParityReplace affects the equivalent field in the DCB structure: ErrorChar, and
363         // the DCB flag fErrorChar.
364         internal byte ParityReplace
365         {
366             //get {   return parityReplace; }
367             set
368             {
369                 if(value != parityReplace)
370                 {
371                     byte parityReplaceOld = parityReplace;
372                     byte errorCharOld = dcb.ErrorChar;
373                     int fErrorCharOld = GetDcbFlag(NativeMethods.FERRORCHAR);
374
375                     parityReplace = value;
376                     if (GetDcbFlag(NativeMethods.FPARITY) == 1)
377                     {
378                         SetDcbFlag(NativeMethods.FERRORCHAR, (parityReplace != '\0')? 1 : 0);
379                         dcb.ErrorChar = parityReplace;
380                     }
381                     else
382                     {
383                         SetDcbFlag(NativeMethods.FERRORCHAR, 0);
384                         dcb.ErrorChar = (byte) '\0';
385                     }
386
387
388                     if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false)
389                     {
390                         parityReplace = parityReplaceOld;
391                         SetDcbFlag(NativeMethods.FERRORCHAR, fErrorCharOld);
392                         dcb.ErrorChar = errorCharOld;
393                         InternalResources.WinIOError();
394                     }
395                 }
396             }
397         }
398
399         // Timeouts are considered to be TOTAL time for the Read/Write operation and to be in milliseconds.
400         // Timeouts are translated into DCB structure as follows:
401         // Desired timeout      =>  ReadTotalTimeoutConstant    ReadTotalTimeoutMultiplier  ReadIntervalTimeout
402         //  0                                   0                           0               MAXDWORD
403         //  0 < n < infinity                    n                       MAXDWORD            MAXDWORD
404         // infinity                             infiniteTimeoutConst    MAXDWORD            MAXDWORD
405         //
406         // rationale for "infinity": There does not exist in the COMMTIMEOUTS structure a way to
407         // *wait indefinitely for any byte, return when found*.  Instead, if we set ReadTimeout
408         // to infinity, SerialStream's EndRead loops if infiniteTimeoutConst mills have elapsed
409         // without a byte received.  Note that this is approximately 24 days, so essentially
410         // most practical purposes effectively equate 24 days with an infinite amount of time
411         // on a serial port connection.
412         public override int ReadTimeout
413         {
414             get
415             {
416                 int constant = commTimeouts.ReadTotalTimeoutConstant;
417
418                 if (constant == infiniteTimeoutConst) return SerialPort.InfiniteTimeout;
419                 else return constant;
420             }
421             set
422             {
423                 if (value < 0 && value != SerialPort.InfiniteTimeout)
424                     throw new ArgumentOutOfRangeException("ReadTimeout", SR.GetString(SR.ArgumentOutOfRange_Timeout));
425                 if (_handle == null) InternalResources.FileNotOpen();
426
427                 int oldReadConstant = commTimeouts.ReadTotalTimeoutConstant;
428                 int oldReadInterval = commTimeouts.ReadIntervalTimeout;
429                 int oldReadMultipler = commTimeouts.ReadTotalTimeoutMultiplier;
430
431                 // NOTE: this logic should match what is in the constructor
432                 if (value == 0) {
433                     commTimeouts.ReadTotalTimeoutConstant   = 0;
434                     commTimeouts.ReadTotalTimeoutMultiplier = 0;
435                     commTimeouts.ReadIntervalTimeout        = NativeMethods.MAXDWORD;
436                 } else if (value == SerialPort.InfiniteTimeout) {
437                     // SetCommTimeouts doesn't like a value of -1 for some reason, so
438                     // we'll use -2(infiniteTimeoutConst) to represent infinite. 
439                     commTimeouts.ReadTotalTimeoutConstant   = infiniteTimeoutConst;
440                     commTimeouts.ReadTotalTimeoutMultiplier = NativeMethods.MAXDWORD;
441                     commTimeouts.ReadIntervalTimeout        = NativeMethods.MAXDWORD;
442                 } else {
443                     commTimeouts.ReadTotalTimeoutConstant   = value;
444                     commTimeouts.ReadTotalTimeoutMultiplier = NativeMethods.MAXDWORD;
445                     commTimeouts.ReadIntervalTimeout        = NativeMethods.MAXDWORD;
446                 }
447
448                 if (UnsafeNativeMethods.SetCommTimeouts(_handle, ref commTimeouts) == false)
449                 {
450                     commTimeouts.ReadTotalTimeoutConstant = oldReadConstant;
451                     commTimeouts.ReadTotalTimeoutMultiplier = oldReadMultipler;
452                     commTimeouts.ReadIntervalTimeout = oldReadInterval;
453                     InternalResources.WinIOError();
454                 }
455             }
456         }
457
458         internal bool RtsEnable
459         {
460             get { 
461                 int fRtsControl = GetDcbFlag(NativeMethods.FRTSCONTROL);
462                 if (fRtsControl == NativeMethods.RTS_CONTROL_HANDSHAKE)
463                     throw new InvalidOperationException(SR.GetString(SR.CantSetRtsWithHandshaking));
464
465                 return (fRtsControl == NativeMethods.RTS_CONTROL_ENABLE);
466             }
467             set
468             {
469                 if ((handshake == Handshake.RequestToSend || handshake == Handshake.RequestToSendXOnXOff))
470                     throw new InvalidOperationException(SR.GetString(SR.CantSetRtsWithHandshaking));
471
472                 if (value != rtsEnable) {
473                     int fRtsControlOld = GetDcbFlag(NativeMethods.FRTSCONTROL);
474
475                     rtsEnable = value;
476                     if(value)
477                         SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_ENABLE);
478                     else
479                         SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_DISABLE);
480
481                     if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false)
482                     {
483                         SetDcbFlag(NativeMethods.FRTSCONTROL, fRtsControlOld);
484                         // set it back to the old value on a failure
485                         rtsEnable = !rtsEnable;
486                         InternalResources.WinIOError();
487                     }
488
489                     if (!UnsafeNativeMethods.EscapeCommFunction(_handle, value ? NativeMethods.SETRTS : NativeMethods.CLRRTS))
490                         InternalResources.WinIOError();
491                 }
492             }
493         }
494
495         // StopBits represented in C# as StopBits enum type and in Win32 as an integer 1, 2, or 3.
496         internal StopBits StopBits
497         {
498             /*get
499             {
500                 switch(dcb.StopBits)
501                 {
502                     case NativeMethods.ONESTOPBIT:
503                         return StopBits.One;
504                     case NativeMethods.ONE5STOPBITS:
505                         return StopBits.OnePointFive;
506                     case NativeMethods.TWOSTOPBITS:
507                         return StopBits.Two;
508                     default:
509                         Debug.Assert(true, "Invalid Stopbits value " + dcb.StopBits);
510                         return StopBits.One;
511
512                 }
513             }
514             */
515             set
516             {
517                 Debug.Assert(!(value < StopBits.One || value > StopBits.OnePointFive), "An invalid value was passed to StopBits");
518
519                 byte nativeValue = 0;
520                 if (value == StopBits.One) nativeValue = (byte) NativeMethods.ONESTOPBIT;
521                 else if (value == StopBits.OnePointFive) nativeValue = (byte) NativeMethods.ONE5STOPBITS;
522                 else nativeValue = (byte) NativeMethods.TWOSTOPBITS;
523
524                 if(nativeValue != dcb.StopBits)
525                 {
526                     byte stopBitsOld = dcb.StopBits;
527                     dcb.StopBits = nativeValue;
528
529                     if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false)
530                     {
531                         dcb.StopBits = stopBitsOld;
532                         InternalResources.WinIOError();
533                     }
534                 }
535             }
536         }
537
538         // note: WriteTimeout must be either SerialPort.InfiniteTimeout or POSITIVE.
539         // a timeout of zero implies that every Write call throws an exception.
540         public override int WriteTimeout
541         {
542             get
543             {
544                 int timeout = commTimeouts.WriteTotalTimeoutConstant;
545                 return (timeout == 0) ? SerialPort.InfiniteTimeout : timeout;
546             }
547             set
548             {
549                 if (value <= 0 && value != SerialPort.InfiniteTimeout)
550                     throw new ArgumentOutOfRangeException("WriteTimeout", SR.GetString(SR.ArgumentOutOfRange_WriteTimeout));
551                 if (_handle == null) InternalResources.FileNotOpen();
552
553                 int oldWriteConstant = commTimeouts.WriteTotalTimeoutConstant;
554                 commTimeouts.WriteTotalTimeoutConstant = ((value == SerialPort.InfiniteTimeout) ? 0 : value);
555
556                 if (UnsafeNativeMethods.SetCommTimeouts(_handle, ref commTimeouts) == false)
557                 {
558                     commTimeouts.WriteTotalTimeoutConstant = oldWriteConstant;
559                     InternalResources.WinIOError();
560                 }
561             }
562         }
563
564
565         // CDHolding, CtsHolding, DsrHolding query the current state of each of the carrier, the CTS pin,
566         // and the DSR pin, respectively. Read-only.
567         // All will throw exceptions if the port is not open.
568         internal bool CDHolding
569         {
570             get
571             {
572                 int pinStatus = 0;
573                 if (UnsafeNativeMethods.GetCommModemStatus(_handle, ref pinStatus) == false)
574                     InternalResources.WinIOError();
575
576                 return (NativeMethods.MS_RLSD_ON & pinStatus) != 0;
577             }
578         }
579
580
581         internal bool CtsHolding
582         {
583             get
584             {
585                 int pinStatus = 0;
586                 if (UnsafeNativeMethods.GetCommModemStatus(_handle, ref pinStatus) == false)
587                     InternalResources.WinIOError();
588                 return (NativeMethods.MS_CTS_ON & pinStatus) != 0;
589             }
590
591         }
592
593         internal bool DsrHolding
594         {
595             get
596             {
597                 int pinStatus = 0;
598                 if (UnsafeNativeMethods.GetCommModemStatus(_handle, ref pinStatus) == false)
599                     InternalResources.WinIOError();
600
601                 return (NativeMethods.MS_DSR_ON & pinStatus) != 0;
602             }
603         }
604
605
606         // Fills comStat structure from an unmanaged function
607         // to determine the number of bytes waiting in the serial driver's internal receive buffer.
608         internal int BytesToRead {
609             get
610             {
611                 int errorCode = 0; // "ref" arguments need to have values, as opposed to "out" arguments
612                 if (UnsafeNativeMethods.ClearCommError(_handle, ref errorCode, ref comStat)  == false)
613                 {
614                     InternalResources.WinIOError();
615                 }
616                 return (int) comStat.cbInQue;
617             }
618         }
619
620         // Fills comStat structure from an unmanaged function
621         // to determine the number of bytes waiting in the serial driver's internal transmit buffer.
622         internal int BytesToWrite {
623             get
624             {
625                 int errorCode = 0; // "ref" arguments need to be set before method invocation, as opposed to "out" arguments
626                 if (UnsafeNativeMethods.ClearCommError(_handle, ref errorCode, ref comStat)  == false)
627                     InternalResources.WinIOError();
628                 return (int) comStat.cbOutQue;
629
630             }
631         }
632
633         // -----------SECTION: constructor --------------------------*
634
635         // this method is used by SerialPort upon SerialStream's creation
636         [ResourceExposure(ResourceScope.Machine)]
637         [ResourceConsumption(ResourceScope.Machine)]
638         internal SerialStream(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits, int readTimeout, int writeTimeout, Handshake handshake,
639             bool dtrEnable, bool rtsEnable, bool discardNull, byte parityReplace)
640         {
641
642             int flags = UnsafeNativeMethods.FILE_FLAG_OVERLAPPED;
643             // disable async on win9x
644             if (Environment.OSVersion.Platform == PlatformID.Win32Windows) {
645                 flags = UnsafeNativeMethods.FILE_ATTRIBUTE_NORMAL;
646                 isAsync = false;
647             }
648
649             if ((portName == null) || !portName.StartsWith("COM", StringComparison.OrdinalIgnoreCase)) 
650                 throw new ArgumentException(SR.GetString(SR.Arg_InvalidSerialPort), "portName");
651
652             //Error checking done in SerialPort.
653
654             SafeFileHandle tempHandle = UnsafeNativeMethods.CreateFile("\\\\.\\" + portName,
655                 NativeMethods.GENERIC_READ | NativeMethods.GENERIC_WRITE,
656                 0,    // comm devices must be opened w/exclusive-access
657                 IntPtr.Zero, // no security attributes
658                 UnsafeNativeMethods.OPEN_EXISTING, // comm devices must use OPEN_EXISTING
659                 flags,
660                 IntPtr.Zero  // hTemplate must be NULL for comm devices
661                 );
662
663             if (tempHandle.IsInvalid)
664             {
665                 InternalResources.WinIOError(portName);
666             }
667
668             try {
669                 int fileType = UnsafeNativeMethods.GetFileType(tempHandle);
670
671                 // Allowing FILE_TYPE_UNKNOWN for legitimate serial device such as USB to serial adapter device 
672                 if ((fileType != UnsafeNativeMethods.FILE_TYPE_CHAR) && (fileType != UnsafeNativeMethods.FILE_TYPE_UNKNOWN))
673                     throw new ArgumentException(SR.GetString(SR.Arg_InvalidSerialPort), "portName");
674
675                 _handle = tempHandle;
676
677                 // set properties of the stream that exist as members in SerialStream
678                 this.portName = portName;
679                 this.handshake = handshake;
680                 this.parityReplace = parityReplace;
681
682                 tempBuf = new byte[1];          // used in ReadByte()
683
684                 // Fill COMMPROPERTIES struct, which has our maximum allowed baud rate.
685                 // Call a serial specific API such as GetCommModemStatus which would fail
686                 // in case the device is not a legitimate serial device. For instance, 
687                 // some illegal FILE_TYPE_UNKNOWN device (or) "LPT1" on Win9x 
688                 // trying to pass for serial will be caught here. GetCommProperties works
689                 // fine for "LPT1" on Win9x, so that alone can't be relied here to
690                 // detect non serial devices.
691                                
692                 commProp = new UnsafeNativeMethods.COMMPROP();
693                 int pinStatus = 0;
694                 
695                 if (!UnsafeNativeMethods.GetCommProperties(_handle, ref commProp) 
696                     || !UnsafeNativeMethods.GetCommModemStatus(_handle, ref pinStatus))
697                 {
698                     // If the portName they have passed in is a FILE_TYPE_CHAR but not a serial port,
699                     // for example "LPT1", this API will fail.  For this reason we handle the error message specially. 
700                     int errorCode = Marshal.GetLastWin32Error();
701                     if ((errorCode == NativeMethods.ERROR_INVALID_PARAMETER) || (errorCode == NativeMethods.ERROR_INVALID_HANDLE))
702                         throw new ArgumentException(SR.GetString(SR.Arg_InvalidSerialPortExtended), "portName");
703                     else 
704                         InternalResources.WinIOError(errorCode, string.Empty);
705                 }
706                 if (commProp.dwMaxBaud != 0 && baudRate > commProp.dwMaxBaud)
707                     throw new ArgumentOutOfRangeException("baudRate", SR.GetString(SR.Max_Baud, commProp.dwMaxBaud));
708
709
710                 comStat = new UnsafeNativeMethods.COMSTAT();
711                 // create internal DCB structure, initialize according to Platform SDK
712                 // standard: ms-help://MS.MSNDNQTR.2002APR.1003/hardware/commun_965u.htm
713                 dcb = new UnsafeNativeMethods.DCB();
714
715                 // set constant properties of the DCB
716                 InitializeDCB(baudRate, parity, dataBits, stopBits, discardNull);
717
718                 this.DtrEnable = dtrEnable;
719
720                 // query and cache the initial RtsEnable value 
721                 // so that set_RtsEnable can do the (value != rtsEnable) optimization
722                 this.rtsEnable = (GetDcbFlag(NativeMethods.FRTSCONTROL) == NativeMethods.RTS_CONTROL_ENABLE);
723
724                 // now set this.RtsEnable to the specified value.
725                 // Handshake takes precedence, this will be a nop if 
726                 // handshake is either RequestToSend or RequestToSendXOnXOff 
727                 if ((handshake != Handshake.RequestToSend && handshake != Handshake.RequestToSendXOnXOff))
728                     this.RtsEnable = rtsEnable;
729
730                 // NOTE: this logic should match what is in the ReadTimeout property
731                 if (readTimeout == 0) {
732                     commTimeouts.ReadTotalTimeoutConstant   = 0;
733                     commTimeouts.ReadTotalTimeoutMultiplier = 0;
734                     commTimeouts.ReadIntervalTimeout        = NativeMethods.MAXDWORD;
735                 } else if (readTimeout == SerialPort.InfiniteTimeout) {
736                     // SetCommTimeouts doesn't like a value of -1 for some reason, so
737                     // we'll use -2(infiniteTimeoutConst) to represent infinite. 
738                     commTimeouts.ReadTotalTimeoutConstant   = infiniteTimeoutConst;
739                     commTimeouts.ReadTotalTimeoutMultiplier = NativeMethods.MAXDWORD;
740                     commTimeouts.ReadIntervalTimeout        = NativeMethods.MAXDWORD;
741                 } else {
742                     commTimeouts.ReadTotalTimeoutConstant   = readTimeout;
743                     commTimeouts.ReadTotalTimeoutMultiplier = NativeMethods.MAXDWORD;
744                     commTimeouts.ReadIntervalTimeout        = NativeMethods.MAXDWORD;
745                 }
746
747                 commTimeouts.WriteTotalTimeoutMultiplier    = 0;
748                 commTimeouts.WriteTotalTimeoutConstant      = ((writeTimeout == SerialPort.InfiniteTimeout) ? 0 : writeTimeout);
749
750                 // set unmanaged timeout structure
751                 if (UnsafeNativeMethods.SetCommTimeouts(_handle, ref commTimeouts) == false)
752                 {
753                     InternalResources.WinIOError();
754                 }
755
756                 if (isAsync) {
757                     if (!ThreadPool.BindHandle(_handle))
758                     {
759                         throw new IOException(SR.GetString(SR.IO_BindHandleFailed));
760                     }
761                 }
762
763                 // monitor all events except TXEMPTY
764                 UnsafeNativeMethods.SetCommMask(_handle, NativeMethods.ALL_EVENTS);
765
766                 // prep. for starting event cycle.
767                 eventRunner = new EventLoopRunner(this);
768                 Thread eventLoopThread = new Thread(new ThreadStart(eventRunner.WaitForCommEvent));
769                 eventLoopThread.IsBackground = true;
770                 eventLoopThread.Start();
771                 
772             }
773             catch  {
774                 // if there are any exceptions after the call to CreateFile, we need to be sure to close the
775                 // handle before we let them continue up.
776                 tempHandle.Close();
777                 _handle = null;
778                 throw;
779             }
780         }
781
782         ~SerialStream()
783         {
784             Dispose(false);
785         }
786
787         protected override void Dispose(bool disposing)
788         {
789             // Signal the other side that we're closing.  Should do regardless of whether we've called
790             // Close() or not Dispose() 
791             if (_handle != null && !_handle.IsInvalid) {
792                 try {
793
794                     eventRunner.endEventLoop = true;
795
796                     Thread.MemoryBarrier();
797
798                     bool skipSPAccess = false;
799
800                     // turn off all events and signal WaitCommEvent
801                     UnsafeNativeMethods.SetCommMask(_handle, 0);
802                     if (!UnsafeNativeMethods.EscapeCommFunction(_handle, NativeMethods.CLRDTR)) 
803                     {
804                         int hr = Marshal.GetLastWin32Error();
805  
806                         // access denied can happen if USB is yanked out. If that happens, we
807                         // want to at least allow finalize to succeed and clean up everything 
808                         // we can. To achieve this, we need to avoid further attempts to access
809                         // the SerialPort.  A customer also reported seeing ERROR_BAD_COMMAND here.
810                         // Do not throw an exception on the finalizer thread - that's just rude,
811                         // since apps can't catch it and we may tear down the app.
812                         const int ERROR_DEVICE_REMOVED = 1617;
813                         if ((hr == NativeMethods.ERROR_ACCESS_DENIED || hr == NativeMethods.ERROR_BAD_COMMAND || hr == ERROR_DEVICE_REMOVED) && !disposing) {
814                             skipSPAccess = true;
815                         }
816                         else {
817                             // should not happen
818                             Contract.Assert(false, String.Format("Unexpected error code from EscapeCommFunction in SerialPort.Dispose(bool)  Error code: 0x{0:x}", (uint)hr));
819                             // Do not throw an exception from the finalizer here.
820                             if (disposing)
821                                 InternalResources.WinIOError();
822                         }
823                     }
824
825                     if (!skipSPAccess && !_handle.IsClosed) {
826                         Flush();
827                     }
828
829                     eventRunner.waitCommEventWaitHandle.Set();
830
831                     if (!skipSPAccess) {
832                         DiscardInBuffer();
833                         DiscardOutBuffer();
834                     }
835
836                     if (disposing && eventRunner != null) {
837                         // now we need to wait for the event loop to tell us it's done.  Without this we could get into a ---- where the
838                         // event loop kept the port open even after Dispose ended.
839                         eventRunner.eventLoopEndedSignal.WaitOne();
840                         eventRunner.eventLoopEndedSignal.Close();
841                         eventRunner.waitCommEventWaitHandle.Close();
842                     }
843                 }
844                 finally {
845                     // If we are disposing synchronize closing with raising SerialPort events
846                     if (disposing) {
847                         lock (this) {
848                             _handle.Close();
849                             _handle = null;
850                         }
851                     }
852                     else {
853                         _handle.Close();
854                         _handle = null;
855                     }
856                     base.Dispose(disposing);
857                 }
858
859             }
860         }
861
862         // -----SECTION: all public methods ------------------*
863
864         // User-accessible async read method.  Returns SerialStreamAsyncResult : IAsyncResult
865         [HostProtection(ExternalThreading=true)]
866         public override IAsyncResult BeginRead(byte[] array, int offset,int numBytes, AsyncCallback userCallback, object stateObject)
867         {
868             if (array==null)
869                 throw new ArgumentNullException("array");
870             if (offset < 0)
871                 throw new ArgumentOutOfRangeException("offset", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired));
872             if (numBytes < 0)
873                 throw new ArgumentOutOfRangeException("numBytes", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired));
874             if (array.Length - offset < numBytes)
875                 throw new ArgumentException(SR.GetString(SR.Argument_InvalidOffLen));
876             if (_handle == null) InternalResources.FileNotOpen();
877
878             int oldtimeout = ReadTimeout;
879             ReadTimeout = SerialPort.InfiniteTimeout;
880             IAsyncResult result;
881             try {
882                 if (!isAsync)
883                     result = base.BeginRead(array, offset, numBytes, userCallback, stateObject);
884                 else
885                     result = BeginReadCore(array, offset, numBytes, userCallback, stateObject);
886
887             }
888             finally {
889                 ReadTimeout = oldtimeout;
890             }
891             return result;
892         }
893
894         // User-accessible async write method.  Returns SerialStreamAsyncResult : IAsyncResult
895         // Throws an exception if port is in break state.
896         [HostProtection(ExternalThreading=true)]
897         public override IAsyncResult BeginWrite(byte[] array, int offset, int numBytes,
898             AsyncCallback userCallback, object stateObject)
899         {
900             if (inBreak)
901                 throw new InvalidOperationException(SR.GetString(SR.In_Break_State));
902             if (array==null)
903                 throw new ArgumentNullException("array");
904             if (offset < 0)
905                 throw new ArgumentOutOfRangeException("offset", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired));
906             if (numBytes < 0)
907                 throw new ArgumentOutOfRangeException("numBytes", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired));
908             if (array.Length - offset < numBytes)
909                 throw new ArgumentException(SR.GetString(SR.Argument_InvalidOffLen));
910             if (_handle == null) InternalResources.FileNotOpen();
911
912             int oldtimeout = WriteTimeout;
913             WriteTimeout = SerialPort.InfiniteTimeout;
914             IAsyncResult result;
915             try {
916                 if (!isAsync)
917                     result = base.BeginWrite(array, offset, numBytes, userCallback, stateObject); 
918                 else
919                     result = BeginWriteCore(array, offset, numBytes, userCallback, stateObject);
920             }
921             finally {
922                 WriteTimeout = oldtimeout;
923             }
924             return result;
925         }
926
927         // Uses Win32 method to dump out the receive buffer; analagous to MSComm's "InBufferCount = 0"
928         internal void DiscardInBuffer()
929         {
930             
931             if (UnsafeNativeMethods.PurgeComm(_handle, NativeMethods.PURGE_RXCLEAR | NativeMethods.PURGE_RXABORT) == false)
932                 InternalResources.WinIOError();
933         }
934
935         // Uses Win32 method to dump out the xmit buffer; analagous to MSComm's "OutBufferCount = 0"
936         internal void DiscardOutBuffer()
937         {
938             if (UnsafeNativeMethods.PurgeComm(_handle, NativeMethods.PURGE_TXCLEAR | NativeMethods.PURGE_TXABORT) == false)
939                 InternalResources.WinIOError();
940         }
941
942         // Async companion to BeginRead.
943         // Note, assumed IAsyncResult argument is of derived type SerialStreamAsyncResult,
944         // and throws an exception if untrue.
945         public unsafe override int EndRead(IAsyncResult asyncResult)
946         {
947             if (!isAsync) 
948                 return base.EndRead(asyncResult);
949
950             if (asyncResult==null)
951                 throw new ArgumentNullException("asyncResult");
952
953             SerialStreamAsyncResult afsar = asyncResult as SerialStreamAsyncResult;
954             if (afsar==null || afsar._isWrite)
955                 InternalResources.WrongAsyncResult();
956
957             // This sidesteps race conditions, avoids memory corruption after freeing the
958             // NativeOverlapped class or GCHandle twice.
959             if (1 == Interlocked.CompareExchange(ref afsar._EndXxxCalled, 1, 0))
960                 InternalResources.EndReadCalledTwice();
961
962             bool failed = false;
963
964             // Obtain the WaitHandle, but don't use public property in case we
965             // delay initialize the manual reset event in the future.
966             WaitHandle wh = afsar._waitHandle;
967             if (wh != null)
968             {
969                 // We must block to ensure that AsyncFSCallback has completed,
970                 // and we should close the WaitHandle in here.  
971                 try {
972                     wh.WaitOne();
973                     Debug.Assert(afsar._isComplete == true, "SerialStream::EndRead - AsyncFSCallback didn't set _isComplete to true!");
974                 
975                     // InfiniteTimeout is not something native to the underlying serial device, 
976                     // we specify the timeout to be a very large value (MAXWORD-1) to achieve 
977                     // an infinite timeout illusion. 
978
979                     // I'm not sure what we can do here after an asyn operation with infinite 
980                     // timeout returns with no data. From a purist point of view we should 
981                     // somehow restart the read operation but we are not in a position to do so
982                     // (and frankly that may not necessarily be the right thing to do here) 
983                     // I think the best option in this (almost impossible to run into) situation 
984                     // is to throw some sort of IOException.
985
986                     if ((afsar._numBytes == 0) && (ReadTimeout == SerialPort.InfiniteTimeout) && (afsar._errorCode == 0))
987                       failed = true;
988                 }
989                 finally {
990                     wh.Close();
991                 }
992             }
993
994             // Free memory, GC handles.
995             NativeOverlapped* overlappedPtr = afsar._overlapped;
996             if (overlappedPtr != null)
997                 Overlapped.Free(overlappedPtr);
998
999             // Check for non-timeout errors during the read.
1000             if (afsar._errorCode != 0) 
1001                 InternalResources.WinIOError(afsar._errorCode, portName);
1002
1003             if (failed)
1004                 throw new IOException(SR.GetString(SR.IO_OperationAborted));
1005             
1006             return afsar._numBytes;
1007         }
1008
1009         // Async companion to BeginWrite.
1010         // Note, assumed IAsyncResult argument is of derived type SerialStreamAsyncResult,
1011         // and throws an exception if untrue.
1012         // Also fails if called in port's break state.
1013         public unsafe override void EndWrite(IAsyncResult asyncResult) {
1014             if (!isAsync) {
1015                 base.EndWrite(asyncResult);
1016                 return;
1017             }
1018
1019             if (inBreak)
1020                 throw new InvalidOperationException(SR.GetString(SR.In_Break_State));
1021             if (asyncResult==null)
1022                 throw new ArgumentNullException("asyncResult");
1023
1024             SerialStreamAsyncResult afsar = asyncResult as SerialStreamAsyncResult;
1025             if (afsar==null || !afsar._isWrite)
1026                 InternalResources.WrongAsyncResult();
1027
1028             // This sidesteps race conditions, avoids memory corruption after freeing the
1029             // NativeOverlapped class or GCHandle twice.
1030             if (1 == Interlocked.CompareExchange(ref afsar._EndXxxCalled, 1, 0))
1031                 InternalResources.EndWriteCalledTwice();
1032
1033             // Obtain the WaitHandle, but don't use public property in case we
1034             // delay initialize the manual reset event in the future.
1035             WaitHandle wh = afsar._waitHandle;
1036             if (wh != null) 
1037             {
1038                 // We must block to ensure that AsyncFSCallback has completed,
1039                 // and we should close the WaitHandle in here.  
1040                 try {
1041                     wh.WaitOne();
1042                     Debug.Assert(afsar._isComplete == true, "SerialStream::EndWrite - AsyncFSCallback didn't set _isComplete to true!");
1043                 }
1044                 finally {
1045                     wh.Close();
1046                 }
1047             }
1048
1049             // Free memory, GC handles.
1050             NativeOverlapped* overlappedPtr = afsar._overlapped;
1051             if (overlappedPtr != null)
1052                 Overlapped.Free(overlappedPtr);
1053
1054             // Now check for any error during the write.
1055             if (afsar._errorCode != 0)
1056                 InternalResources.WinIOError(afsar._errorCode, portName);
1057
1058             // Number of bytes written is afsar._numBytes.
1059         }
1060
1061         // Flush dumps the contents of the serial driver's internal read and write buffers.
1062         // We actually expose the functionality for each, but fulfilling Stream's contract
1063         // requires a Flush() method.  Fails if handle closed.
1064         // Note: Serial driver's write buffer is *already* attempting to write it, so we can only wait until it finishes.
1065         public override void Flush() 
1066         { 
1067             if (_handle == null) throw new ObjectDisposedException(SR.GetString(SR.Port_not_open));
1068             UnsafeNativeMethods.FlushFileBuffers(_handle);
1069         }
1070
1071         // Blocking read operation, returning the number of bytes read from the stream.
1072
1073         public override int Read([In, Out] byte[] array, int offset, int count)
1074         {
1075             return Read(array, offset, count, ReadTimeout);
1076         }
1077
1078         internal unsafe int Read([In, Out] byte[] array, int offset, int count, int timeout)
1079         {
1080             if (array==null)
1081                 throw new ArgumentNullException("array", SR.GetString(SR.ArgumentNull_Buffer));
1082             if (offset < 0)
1083                 throw new ArgumentOutOfRangeException("offset", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired));
1084             if (count < 0)
1085                 throw new ArgumentOutOfRangeException("count", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired));
1086             if (array.Length - offset < count)
1087                 throw new ArgumentException(SR.GetString(SR.Argument_InvalidOffLen));
1088             if (count == 0) return 0; // return immediately if no bytes requested; no need for overhead.
1089
1090             Debug.Assert(timeout == SerialPort.InfiniteTimeout || timeout >= 0, "Serial Stream Read - called with timeout " + timeout);
1091
1092             // Check to see we have no handle-related error, since the port's always supposed to be open.
1093             if (_handle == null) InternalResources.FileNotOpen();
1094
1095             int numBytes = 0;
1096             int hr;
1097             if (isAsync) {
1098                 IAsyncResult result = BeginReadCore(array, offset, count, null, null);
1099                 numBytes = EndRead(result);
1100             }
1101             else {
1102                 numBytes = ReadFileNative(array, offset, count, null, out hr);
1103                 if (numBytes == -1) {
1104                     InternalResources.WinIOError();
1105                 }
1106             }
1107
1108             if (numBytes == 0)
1109                 throw new TimeoutException();
1110
1111             return numBytes;
1112         }
1113
1114         public override int ReadByte()
1115         {
1116             return ReadByte(ReadTimeout);
1117         }
1118
1119         internal unsafe int ReadByte(int timeout)
1120         {
1121             if (_handle == null) InternalResources.FileNotOpen();
1122
1123             int numBytes = 0;
1124             int hr;
1125             if (isAsync) {
1126                 IAsyncResult result = BeginReadCore(tempBuf, 0, 1, null, null);
1127                 numBytes = EndRead(result);
1128             }
1129             else {
1130                 numBytes = ReadFileNative(tempBuf, 0, 1, null, out hr);
1131                 if (numBytes == -1) {
1132                     InternalResources.WinIOError();
1133                 }
1134             }
1135
1136             if (numBytes == 0)
1137                 throw new TimeoutException();
1138             else
1139                 return tempBuf[0];
1140         }
1141
1142         public override long Seek(long offset, SeekOrigin origin)
1143         {
1144             throw new NotSupportedException(SR.GetString(SR.NotSupported_UnseekableStream));
1145         }
1146
1147         public override void SetLength(long value)
1148         {
1149             throw new NotSupportedException(SR.GetString(SR.NotSupported_UnseekableStream));
1150         }
1151
1152         internal void SetBufferSizes(int readBufferSize, int writeBufferSize) {
1153             if (_handle == null) InternalResources.FileNotOpen();
1154
1155             if (!UnsafeNativeMethods.SetupComm(_handle, readBufferSize, writeBufferSize))
1156                 InternalResources.WinIOError();
1157         }
1158
1159         public override void Write(byte[] array, int offset, int count)
1160         {
1161             Write(array, offset, count, WriteTimeout);
1162         }
1163         
1164         internal unsafe void Write(byte[] array, int offset, int count, int timeout)
1165         {
1166
1167             if (inBreak)
1168                 throw new InvalidOperationException(SR.GetString(SR.In_Break_State));
1169             if (array==null)
1170                 throw new ArgumentNullException("buffer", SR.GetString(SR.ArgumentNull_Array));
1171             if (offset < 0)
1172                 throw new ArgumentOutOfRangeException("offset", SR.GetString(SR.ArgumentOutOfRange_NeedPosNum));
1173             if (count < 0)
1174                 throw new ArgumentOutOfRangeException("count", SR.GetString(SR.ArgumentOutOfRange_NeedPosNum));
1175             if (count == 0) return; // no need to expend overhead in creating asyncResult, etc.
1176             if (array.Length - offset < count)
1177                 throw new ArgumentException("count",SR.GetString(SR.ArgumentOutOfRange_OffsetOut));
1178             Debug.Assert(timeout == SerialPort.InfiniteTimeout || timeout >= 0, "Serial Stream Write - write timeout is " + timeout);
1179
1180             // check for open handle, though the port is always supposed to be open
1181             if (_handle == null) InternalResources.FileNotOpen();
1182
1183             int numBytes;
1184             int hr;
1185             if (isAsync) {
1186                 IAsyncResult result = BeginWriteCore(array, offset, count, null, null);
1187                 EndWrite(result);
1188
1189                 SerialStreamAsyncResult afsar = result as SerialStreamAsyncResult;
1190                 Debug.Assert(afsar != null, "afsar should be a SerialStreamAsyncResult and should not be null");
1191                 numBytes = afsar._numBytes;
1192             } 
1193             else {
1194                 numBytes = WriteFileNative(array, offset, count, null, out hr);
1195                 if (numBytes == -1) {
1196
1197                     // This is how writes timeout on Win9x. 
1198                     if (hr == NativeMethods.ERROR_COUNTER_TIMEOUT)
1199                         throw new TimeoutException(SR.GetString(SR.Write_timed_out));
1200
1201                     InternalResources.WinIOError();
1202                 }
1203             }
1204             
1205             if (numBytes == 0)
1206                 throw new TimeoutException(SR.GetString(SR.Write_timed_out));
1207
1208         }
1209
1210         // use default timeout as argument to WriteByte override with timeout arg
1211         public override void WriteByte(byte value)
1212         {
1213             WriteByte(value, WriteTimeout);
1214         }
1215
1216         internal unsafe void WriteByte(byte value, int timeout)
1217         {
1218             if (inBreak)
1219                 throw new InvalidOperationException(SR.GetString(SR.In_Break_State));
1220
1221             if (_handle == null) InternalResources.FileNotOpen();
1222             tempBuf[0] = value;
1223
1224
1225             int numBytes;
1226             int hr;
1227             if (isAsync) {
1228                 IAsyncResult result = BeginWriteCore(tempBuf, 0, 1, null, null);
1229                 EndWrite(result);
1230
1231                 SerialStreamAsyncResult afsar = result as SerialStreamAsyncResult;
1232                 Debug.Assert(afsar != null, "afsar should be a SerialStreamAsyncResult and should not be null");
1233                 numBytes = afsar._numBytes;
1234             }
1235             else {
1236                 numBytes = WriteFileNative(tempBuf, 0, 1, null, out hr);
1237                 if (numBytes == -1) {
1238                     // This is how writes timeout on Win9x. 
1239                     if (Marshal.GetLastWin32Error() == NativeMethods.ERROR_COUNTER_TIMEOUT)
1240                         throw new TimeoutException(SR.GetString(SR.Write_timed_out));
1241
1242                     InternalResources.WinIOError();
1243                 }
1244             }
1245             
1246             if (numBytes == 0)
1247                 throw new TimeoutException(SR.GetString(SR.Write_timed_out));
1248
1249             return;
1250         }
1251
1252
1253
1254         // --------SUBSECTION: internal-use methods ----------------------*
1255         // ------ internal DCB-supporting methods ------- *
1256
1257         // Initializes unmananged DCB struct, to be called after opening communications resource.
1258         // assumes we have already: baudRate, parity, dataBits, stopBits
1259         // should only be called in SerialStream(...)
1260         private void InitializeDCB(int baudRate, Parity parity, int dataBits, StopBits stopBits, bool discardNull)
1261         {
1262
1263             // first get the current dcb structure setup
1264             if (UnsafeNativeMethods.GetCommState(_handle, ref dcb) == false)
1265             {
1266                 InternalResources.WinIOError();
1267             }
1268             dcb.DCBlength = (uint) System.Runtime.InteropServices.Marshal.SizeOf(dcb);
1269
1270             // set parameterized properties
1271             dcb.BaudRate = (uint) baudRate;
1272             dcb.ByteSize = (byte) dataBits;
1273
1274
1275             switch (stopBits)
1276             {
1277                 case StopBits.One:
1278                     dcb.StopBits = NativeMethods.ONESTOPBIT;
1279                     break;
1280                 case StopBits.OnePointFive:
1281                     dcb.StopBits = NativeMethods.ONE5STOPBITS;
1282                     break;
1283                 case StopBits.Two:
1284                     dcb.StopBits = NativeMethods.TWOSTOPBITS;
1285                     break;
1286                 default:
1287                     Debug.Assert(false, "Invalid value for stopBits");
1288                     break;
1289             }
1290
1291             dcb.Parity = (byte) parity;
1292             // SetDcbFlag, GetDcbFlag expose access to each of the relevant bits of the 32-bit integer
1293             // storing all flags of the DCB.  C# provides no direct means of manipulating bit fields, so
1294             // this is the solution.
1295             SetDcbFlag(NativeMethods.FPARITY, ((parity == Parity.None)  ?  0  :  1));
1296
1297             SetDcbFlag(NativeMethods.FBINARY, 1);   // always true for communications resources
1298
1299             // set DCB fields implied by default and the arguments given.
1300             // Boolean fields in C# must become 1, 0 to properly set the bit flags in the unmanaged DCB struct
1301
1302             SetDcbFlag(NativeMethods.FOUTXCTSFLOW, ((handshake == Handshake.RequestToSend ||
1303                 handshake == Handshake.RequestToSendXOnXOff) ? 1 : 0));
1304             // SetDcbFlag(NativeMethods.FOUTXDSRFLOW, (dsrTimeout != 0L) ? 1 : 0);
1305             SetDcbFlag(NativeMethods.FOUTXDSRFLOW, 0); // dsrTimeout is always set to 0.
1306             SetDcbFlag(NativeMethods.FDTRCONTROL, NativeMethods.DTR_CONTROL_DISABLE);
1307             SetDcbFlag(NativeMethods.FDSRSENSITIVITY, 0); // this should remain off
1308             SetDcbFlag(NativeMethods.FINX, (handshake == Handshake.XOnXOff || handshake == Handshake.RequestToSendXOnXOff) ? 1 : 0);
1309             SetDcbFlag(NativeMethods.FOUTX,(handshake == Handshake.XOnXOff || handshake == Handshake.RequestToSendXOnXOff) ? 1 : 0);
1310
1311             // if no parity, we have no error character (i.e. ErrorChar = '\0' or null character)
1312             if (parity != Parity.None)
1313             {
1314                 SetDcbFlag(NativeMethods.FERRORCHAR, (parityReplace != '\0') ? 1 : 0);
1315                 dcb.ErrorChar = parityReplace;
1316             }
1317             else
1318             {
1319                 SetDcbFlag(NativeMethods.FERRORCHAR, 0);
1320                 dcb.ErrorChar = (byte) '\0';
1321             }
1322
1323             // this method only runs once in the constructor, so we only have the default value to use.
1324             // Later the user may change this via the NullDiscard property.
1325             SetDcbFlag(NativeMethods.FNULL, discardNull ? 1 : 0);
1326
1327
1328             // Setting RTS control, which is RTS_CONTROL_HANDSHAKE if RTS / RTS-XOnXOff handshaking
1329             // used, RTS_ENABLE (RTS pin used during operation) if rtsEnable true but XOnXoff / No handshaking
1330             // used, and disabled otherwise.
1331             if ((handshake == Handshake.RequestToSend ||
1332                 handshake == Handshake.RequestToSendXOnXOff))
1333             {
1334                 SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_HANDSHAKE);
1335             }
1336             else if (GetDcbFlag(NativeMethods.FRTSCONTROL) == NativeMethods.RTS_CONTROL_HANDSHAKE)
1337             {
1338                 SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_DISABLE);
1339             }
1340
1341             dcb.XonChar = NativeMethods.DEFAULTXONCHAR;             // may be exposed later but for now, constant
1342             dcb.XoffChar = NativeMethods.DEFAULTXOFFCHAR;
1343
1344             // minimum number of bytes allowed in each buffer before flow control activated
1345             // heuristically, this has been set at 1/4 of the buffer size
1346             dcb.XonLim = dcb.XoffLim = (ushort) (commProp.dwCurrentRxQueue / 4);
1347
1348             dcb.EofChar = NativeMethods.EOFCHAR;
1349
1350             //OLD MSCOMM: dcb.EvtChar = (byte) 0;
1351             // now changed to make use of RXFlag WaitCommEvent event => Eof WaitForCommEvent event
1352             dcb.EvtChar = NativeMethods.EOFCHAR;
1353
1354             // set DCB structure
1355             if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false)
1356             {
1357                 InternalResources.WinIOError();
1358             }
1359         }
1360
1361         // Here we provide a method for getting the flags of the Device Control Block structure dcb
1362         // associated with each instance of SerialStream, i.e. this method gets myStream.dcb.Flags
1363         // Flags are any of the constants in NativeMethods such as FBINARY, FDTRCONTROL, etc.
1364         internal int GetDcbFlag(int whichFlag)
1365         {
1366             uint mask;
1367
1368             Debug.Assert(whichFlag >= NativeMethods.FBINARY && whichFlag <= NativeMethods.FDUMMY2, "GetDcbFlag needs to fit into enum!");
1369
1370             if (whichFlag == NativeMethods.FDTRCONTROL || whichFlag == NativeMethods.FRTSCONTROL)
1371             {
1372                 mask = 0x3;
1373             }
1374             else if (whichFlag == NativeMethods.FDUMMY2)
1375             {
1376                 mask = 0x1FFFF;
1377             }
1378             else
1379             {
1380                 mask = 0x1;
1381             }
1382             uint result = dcb.Flags & (mask << whichFlag);
1383             return (int) (result >> whichFlag);
1384         }
1385
1386         // Since C# applications have to provide a workaround for accessing and setting bitfields in unmanaged code,
1387         // here we provide methods for getting and setting the Flags field of the Device Control Block structure dcb
1388         // associated with each instance of SerialStream, i.e. this method sets myStream.dcb.Flags
1389         // Flags are any of the constants in NativeMethods such as FBINARY, FDTRCONTROL, etc.
1390         internal void SetDcbFlag(int whichFlag, int setting)
1391         {
1392             uint mask;
1393             setting = setting << whichFlag;
1394
1395             Debug.Assert(whichFlag >= NativeMethods.FBINARY && whichFlag <= NativeMethods.FDUMMY2, "SetDcbFlag needs to fit into enum!");
1396
1397             if (whichFlag == NativeMethods.FDTRCONTROL || whichFlag == NativeMethods.FRTSCONTROL)
1398             {
1399                 mask = 0x3;
1400             }
1401             else if (whichFlag == NativeMethods.FDUMMY2)
1402             {
1403                 mask = 0x1FFFF;
1404             }
1405             else
1406             {
1407                 mask = 0x1;
1408             }
1409
1410             // clear the region
1411             dcb.Flags &= ~(mask << whichFlag);
1412
1413             // set the region
1414             dcb.Flags |= ((uint) setting);
1415         }
1416
1417         // ----SUBSECTION: internal methods supporting public read/write methods-------*
1418
1419         [ResourceExposure(ResourceScope.None)]
1420         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1421         unsafe private SerialStreamAsyncResult BeginReadCore(byte[] array, int offset, int numBytes, AsyncCallback userCallback, Object stateObject)
1422         {
1423
1424             // Create and store async stream class library specific data in the
1425             // async result
1426             SerialStreamAsyncResult asyncResult = new SerialStreamAsyncResult();
1427             asyncResult._userCallback = userCallback;
1428             asyncResult._userStateObject = stateObject;
1429             asyncResult._isWrite = false;
1430
1431             // For Synchronous IO, I could go with either a callback and using
1432             // the managed Monitor class, or I could create a handle and wait on it.
1433             ManualResetEvent waitHandle = new ManualResetEvent(false);
1434             asyncResult._waitHandle = waitHandle;
1435
1436             // Create a managed overlapped class
1437             // We will set the file offsets later
1438             Overlapped overlapped = new Overlapped(0, 0, IntPtr.Zero, asyncResult);
1439
1440             // Pack the Overlapped class, and store it in the async result
1441             NativeOverlapped* intOverlapped = overlapped.Pack(IOCallback, array);
1442
1443             asyncResult._overlapped = intOverlapped;
1444
1445             // queue an async ReadFile operation and pass in a packed overlapped
1446             //int r = ReadFile(_handle, array, numBytes, null, intOverlapped);
1447             int hr = 0;
1448             int r = ReadFileNative(array, offset, numBytes,
1449              intOverlapped, out hr);
1450
1451             // ReadFile, the OS version, will return 0 on failure.  But
1452             // my ReadFileNative wrapper returns -1.  My wrapper will return
1453             // the following:
1454             // On error, r==-1.
1455             // On async requests that are still pending, r==-1 w/ hr==ERROR_IO_PENDING
1456             // on async requests that completed sequentially, r==0
1457             // Note that you will NEVER RELIABLY be able to get the number of bytes
1458             // read back from this call when using overlapped structures!  You must
1459             // not pass in a non-null lpNumBytesRead to ReadFile when using
1460             // overlapped structures!
1461             if (r==-1)
1462             {
1463                 if (hr != NativeMethods.ERROR_IO_PENDING)
1464                 {
1465                     if (hr == NativeMethods.ERROR_HANDLE_EOF)
1466                         InternalResources.EndOfFile();
1467                     else
1468                         InternalResources.WinIOError(hr, String.Empty);
1469                 }
1470             }
1471
1472             return asyncResult;
1473         }
1474
1475         [ResourceExposure(ResourceScope.None)]
1476         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1477         unsafe private SerialStreamAsyncResult BeginWriteCore(byte[] array, int offset, int numBytes, AsyncCallback userCallback, Object stateObject)
1478         {
1479             // Create and store async stream class library specific data in the
1480             // async result
1481             SerialStreamAsyncResult asyncResult = new SerialStreamAsyncResult();
1482             asyncResult._userCallback = userCallback;
1483             asyncResult._userStateObject = stateObject;
1484             asyncResult._isWrite = true;
1485
1486             // For Synchronous IO, I could go with either a callback and using
1487             // the managed Monitor class, or I could create a handle and wait on it.
1488             ManualResetEvent waitHandle = new ManualResetEvent(false);
1489             asyncResult._waitHandle = waitHandle;
1490
1491             // Create a managed overlapped class
1492             // We will set the file offsets later
1493             Overlapped overlapped = new Overlapped(0, 0, IntPtr.Zero, asyncResult);
1494
1495             // Pack the Overlapped class, and store it in the async result
1496             NativeOverlapped* intOverlapped = overlapped.Pack(IOCallback, array);
1497
1498             asyncResult._overlapped = intOverlapped;
1499
1500             int hr = 0;
1501             // queue an async WriteFile operation and pass in a packed overlapped
1502             int r = WriteFileNative(array, offset, numBytes, intOverlapped, out hr);
1503
1504             // WriteFile, the OS version, will return 0 on failure.  But
1505             // my WriteFileNative wrapper returns -1.  My wrapper will return
1506             // the following:
1507             // On error, r==-1.
1508             // On async requests that are still pending, r==-1 w/ hr==ERROR_IO_PENDING
1509             // On async requests that completed sequentially, r==0
1510             // Note that you will NEVER RELIABLY be able to get the number of bytes
1511             // written back from this call when using overlapped IO!  You must
1512             // not pass in a non-null lpNumBytesWritten to WriteFile when using
1513             // overlapped structures!
1514             if (r==-1)
1515             {
1516                 if (hr != NativeMethods.ERROR_IO_PENDING)
1517                 {
1518
1519                     if (hr == NativeMethods.ERROR_HANDLE_EOF)
1520                         InternalResources.EndOfFile();
1521                     else
1522                         InternalResources.WinIOError(hr, String.Empty);
1523                 }
1524             }
1525             return asyncResult;
1526         }
1527
1528
1529         // Internal method, wrapping the PInvoke to ReadFile().
1530         private unsafe int ReadFileNative(byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int hr)
1531         {
1532
1533             // Don't corrupt memory when multiple threads are erroneously writing
1534             // to this stream simultaneously.
1535             if (bytes.Length - offset < count)
1536                 throw new IndexOutOfRangeException(SR.GetString(SR.IndexOutOfRange_IORaceCondition));
1537
1538             // You can't use the fixed statement on an array of length 0.
1539             if (bytes.Length==0)
1540             {
1541                 hr = 0;
1542                 return 0;
1543             }
1544
1545             int r = 0;
1546             int numBytesRead = 0;
1547
1548             fixed(byte* p = bytes)
1549             {
1550                 if (isAsync)
1551                     r = UnsafeNativeMethods.ReadFile(_handle, p + offset, count, IntPtr.Zero, overlapped);
1552                 else
1553                     r = UnsafeNativeMethods.ReadFile(_handle, p + offset, count, out numBytesRead, IntPtr.Zero);
1554             }
1555
1556             if (r==0)
1557             {
1558                 hr = Marshal.GetLastWin32Error();
1559
1560                 // Note: we should never silently ignore an error here without some
1561                 // extra work.  We must make sure that BeginReadCore won't return an
1562                 // IAsyncResult that will cause EndRead to block, since the OS won't
1563                 // call AsyncFSCallback for us.
1564
1565                 // For invalid handles, detect the error and mark our handle
1566                 // as closed to give slightly better error messages.  Also
1567                 // help ensure we avoid handle recycling bugs.
1568                 if (hr == NativeMethods.ERROR_INVALID_HANDLE)
1569                     _handle.SetHandleAsInvalid();
1570
1571                 return -1;
1572             }
1573             else
1574                 hr = 0;
1575             return numBytesRead;
1576         }
1577
1578         private unsafe int WriteFileNative(byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int hr)
1579         {
1580
1581             // Don't corrupt memory when multiple threads are erroneously writing
1582             // to this stream simultaneously.  (Note that the OS is reading from
1583             // the array we pass to WriteFile, but if we read beyond the end and
1584             // that memory isn't allocated, we could get an AV.)
1585             if (bytes.Length - offset < count)
1586                 throw new IndexOutOfRangeException(SR.GetString(SR.IndexOutOfRange_IORaceCondition));
1587
1588             // You can't use the fixed statement on an array of length 0.
1589             if (bytes.Length==0)
1590             {
1591                 hr = 0;
1592                 return 0;
1593             }
1594
1595             int numBytesWritten = 0;
1596             int r = 0;
1597
1598             fixed(byte* p = bytes)
1599             {
1600                 if (isAsync)
1601                     r = UnsafeNativeMethods.WriteFile(_handle, p + offset, count, IntPtr.Zero, overlapped);
1602                 else
1603                     r = UnsafeNativeMethods.WriteFile(_handle, p + offset, count, out numBytesWritten, IntPtr.Zero);
1604             }
1605
1606             if (r==0)
1607             {
1608                 hr = Marshal.GetLastWin32Error();
1609                 // Note: we should never silently ignore an error here without some
1610                 // extra work.  We must make sure that BeginWriteCore won't return an
1611                 // IAsyncResult that will cause EndWrite to block, since the OS won't
1612                 // call AsyncFSCallback for us.
1613
1614                 // For invalid handles, detect the error and mark our handle
1615                 // as closed to give slightly better error messages.  Also
1616                 // help ensure we avoid handle recycling bugs.
1617                 if (hr == NativeMethods.ERROR_INVALID_HANDLE)
1618                     _handle.SetHandleAsInvalid();
1619
1620                 return -1;
1621             }
1622             else
1623                 hr = 0;
1624             return numBytesWritten;
1625         }
1626
1627         // ----SUBSECTION: internal methods supporting events/async operation------*
1628
1629         // This is a the callback prompted when a thread completes any async I/O operation.
1630         unsafe private static void AsyncFSCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped)
1631         {
1632             // Unpack overlapped
1633             Overlapped overlapped = Overlapped.Unpack(pOverlapped);
1634
1635             // Extract async the result from overlapped structure
1636             SerialStreamAsyncResult asyncResult =
1637                 (SerialStreamAsyncResult)overlapped.AsyncResult;
1638             asyncResult._numBytes = (int)numBytes;
1639
1640             asyncResult._errorCode = (int)errorCode;
1641
1642             // Call the user-provided callback.  Note that it can and often should
1643             // call EndRead or EndWrite.  There's no reason to use an async
1644             // delegate here - we're already on a threadpool thread.
1645             // Note the IAsyncResult's completedSynchronously property must return
1646             // false here, saying the user callback was called on another thread.
1647             asyncResult._completedSynchronously = false;
1648             asyncResult._isComplete = true;
1649
1650             // The OS does not signal this event.  We must do it ourselves.
1651             // But don't close it if the user callback called EndXxx, 
1652             // which then closed the manual reset event already.
1653             ManualResetEvent wh = asyncResult._waitHandle;
1654             if (wh != null) {
1655                 bool r = wh.Set();
1656                 if (!r) InternalResources.WinIOError();
1657             }
1658
1659             AsyncCallback userCallback = asyncResult._userCallback;
1660             if (userCallback != null)
1661                 userCallback(asyncResult);
1662         }
1663
1664
1665         // ----SECTION: internal classes --------*
1666
1667         internal sealed class EventLoopRunner {
1668             private WeakReference streamWeakReference;
1669             internal ManualResetEvent eventLoopEndedSignal = new ManualResetEvent(false);
1670             internal ManualResetEvent waitCommEventWaitHandle = new ManualResetEvent(false);
1671             private SafeFileHandle handle = null;
1672             private bool isAsync;
1673             internal bool endEventLoop;
1674             private int eventsOccurred;
1675
1676             WaitCallback callErrorEvents;
1677             WaitCallback callReceiveEvents;
1678             WaitCallback callPinEvents;
1679             IOCompletionCallback freeNativeOverlappedCallback;
1680
1681 #if DEBUG 
1682             private readonly string portName;
1683 #endif            
1684             internal unsafe EventLoopRunner(SerialStream stream) {
1685                 handle = stream._handle;
1686                 streamWeakReference = new WeakReference(stream);
1687
1688                 callErrorEvents = new WaitCallback(CallErrorEvents);
1689                 callReceiveEvents = new WaitCallback(CallReceiveEvents );
1690                 callPinEvents = new WaitCallback(CallPinEvents);
1691                 freeNativeOverlappedCallback = new IOCompletionCallback(FreeNativeOverlappedCallback);
1692                 isAsync = stream.isAsync;
1693 #if DEBUG 
1694                 portName = stream.portName;
1695 #endif            
1696             }
1697
1698             internal bool ShutdownLoop {
1699                 get {
1700                     return endEventLoop;
1701                 }
1702             }
1703             
1704             // This is the blocking method that waits for an event to occur.  It wraps the SDK's WaitCommEvent function.
1705             [ResourceExposure(ResourceScope.None)]
1706             [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
1707             [SuppressMessage("Microsoft.Interoperability", "CA1404:CallGetLastErrorImmediatelyAfterPInvoke", Justification = "this is debug-only code")]
1708             internal unsafe void WaitForCommEvent()
1709             {
1710                 int unused = 0;
1711                 bool doCleanup = false;
1712                 NativeOverlapped* intOverlapped = null;
1713                 while (!ShutdownLoop) {
1714                     SerialStreamAsyncResult asyncResult = null;
1715                     if (isAsync) {
1716                         asyncResult = new SerialStreamAsyncResult();
1717                         asyncResult._userCallback = null;
1718                         asyncResult._userStateObject = null;
1719                         asyncResult._isWrite = false;
1720
1721                         // we're going to use _numBytes for something different in this loop.  In this case, both 
1722                         // freeNativeOverlappedCallback and this thread will decrement that value.  Whichever one decrements it
1723                         // to zero will be the one to free the native overlapped.  This guarantees the overlapped gets freed
1724                         // after both the callback and GetOverlappedResult have had a chance to use it. 
1725                         asyncResult._numBytes = 2;
1726                         asyncResult._waitHandle = waitCommEventWaitHandle;
1727
1728                         waitCommEventWaitHandle.Reset();
1729                         Overlapped overlapped = new Overlapped(0, 0, waitCommEventWaitHandle.SafeWaitHandle.DangerousGetHandle(), asyncResult);
1730                         // Pack the Overlapped class, and store it in the async result
1731                         intOverlapped = overlapped.Pack(freeNativeOverlappedCallback, null);
1732                     }
1733
1734                     fixed (int* eventsOccurredPtr = &eventsOccurred) {
1735
1736                         if (UnsafeNativeMethods.WaitCommEvent(handle, eventsOccurredPtr, intOverlapped) == false)
1737                         {
1738                             int hr = Marshal.GetLastWin32Error();
1739                             // When a device is disconnected unexpectedly from a serial port, there appear to be
1740                             // at least three error codes Windows or drivers may return.
1741                             const int ERROR_DEVICE_REMOVED = 1617;
1742                             if (hr == NativeMethods.ERROR_ACCESS_DENIED || hr == NativeMethods.ERROR_BAD_COMMAND || hr == ERROR_DEVICE_REMOVED) {
1743                                 doCleanup = true;
1744                                 break;
1745                             }
1746                             if (hr == NativeMethods.ERROR_IO_PENDING)
1747                             {
1748                                 Debug.Assert(isAsync, "The port is not open for async, so we should not get ERROR_IO_PENDING from WaitCommEvent");
1749                                 int error;
1750                                     
1751                                 // if we get IO pending, MSDN says we should wait on the WaitHandle, then call GetOverlappedResult
1752                                 // to get the results of WaitCommEvent. 
1753                                 bool success = waitCommEventWaitHandle.WaitOne();
1754                                 Debug.Assert(success, "waitCommEventWaitHandle.WaitOne() returned error " + Marshal.GetLastWin32Error());
1755
1756                                 do { 
1757                                     // NOTE: GetOverlappedResult will modify the original pointer passed into WaitCommEvent.
1758                                     success = UnsafeNativeMethods.GetOverlappedResult(handle, intOverlapped, ref unused, false);
1759                                     error = Marshal.GetLastWin32Error();
1760                                 }
1761                                 while (error == NativeMethods.ERROR_IO_INCOMPLETE && !ShutdownLoop && !success);
1762
1763                                 if (!success) {
1764                                     // Ignore ERROR_IO_INCOMPLETE and ERROR_INVALID_PARAMETER, because there's a chance we'll get
1765                                     // one of those while shutting down 
1766                                     if (! ( (error == NativeMethods.ERROR_IO_INCOMPLETE || error == NativeMethods.ERROR_INVALID_PARAMETER) && ShutdownLoop))
1767                                         Debug.Assert(false, "GetOverlappedResult returned error, we might leak intOverlapped memory" + error.ToString(CultureInfo.InvariantCulture));
1768                                 }
1769                             }
1770                             else if (hr != NativeMethods.ERROR_INVALID_PARAMETER) {
1771                                 // ignore ERROR_INVALID_PARAMETER errors.  WaitCommError seems to return this
1772                                 // when SetCommMask is changed while it's blocking (like we do in Dispose())
1773                                 Debug.Assert(false, "WaitCommEvent returned error " + hr);
1774                             }
1775                         }
1776                     }
1777
1778                     if (!ShutdownLoop)
1779                         CallEvents(eventsOccurred);
1780
1781                     if (isAsync) {   
1782                         if (Interlocked.Decrement(ref asyncResult._numBytes) == 0)
1783                             Overlapped.Free(intOverlapped);
1784                     }
1785                 } // while (!ShutdownLoop)
1786
1787                 if (doCleanup) {
1788                     // the rest will be handled in Dispose()
1789                     endEventLoop = true;
1790                     Overlapped.Free(intOverlapped);
1791                 }
1792                 eventLoopEndedSignal.Set();
1793             }
1794
1795             private unsafe void FreeNativeOverlappedCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped) {
1796                 // Unpack overlapped
1797                 Overlapped overlapped = Overlapped.Unpack(pOverlapped);
1798                 
1799                 // Extract the async result from overlapped structure
1800                 SerialStreamAsyncResult asyncResult =
1801                     (SerialStreamAsyncResult)overlapped.AsyncResult;
1802
1803                 if (Interlocked.Decrement(ref asyncResult._numBytes) == 0)
1804                     Overlapped.Free(pOverlapped);
1805             }
1806             
1807             private void CallEvents(int nativeEvents)
1808             {
1809                 // EV_ERR includes only CE_FRAME, CE_OVERRUN, and CE_RXPARITY
1810                 // To catch errors such as CE_RXOVER, we need to call CleanCommErrors bit more regularly. 
1811                 // EV_RXCHAR is perhaps too loose an event to look for overflow errors but a safe side to err...
1812                 if ((nativeEvents & (NativeMethods.EV_ERR | NativeMethods.EV_RXCHAR)) != 0) {
1813                     int errors = 0;
1814                     if (UnsafeNativeMethods.ClearCommError(handle, ref errors, IntPtr.Zero) == false) {
1815
1816                         //InternalResources.WinIOError();
1817
1818                         // We don't want to throw an exception from the background thread which is un-catchable and hence tear down the process.
1819                         // At present we don't have a first class event that we can raise for this class of fatal errors. One possibility is 
1820                         // to overload SeralErrors event to include another enum (perhaps CE_IOE) that we can use for this purpose. 
1821                         // In the absene of that, it is better to eat this error silently than tearing down the process (lesser of the evil). 
1822                         // This uncleared comm error will most likely ---- up when the device is accessed by other APIs (such as Read) on the 
1823                         // main thread and hence become known. It is bit roundabout but acceptable.  
1824                         //  
1825                         // Shutdown the event runner loop (probably bit drastic but we did come across a fatal error). 
1826                         // Defer actual dispose chores until finalization though. 
1827                         endEventLoop = true;
1828                         Thread.MemoryBarrier();
1829                         return;
1830                     }
1831
1832                     errors = errors & errorEvents;
1833                     // 
1834
1835
1836
1837                     if (errors != 0) {
1838                         ThreadPool.QueueUserWorkItem(callErrorEvents, errors);
1839                     }
1840                 }
1841             
1842                 // now look for pin changed and received events.
1843                 if ((nativeEvents & pinChangedEvents) != 0) {
1844                     ThreadPool.QueueUserWorkItem(callPinEvents, nativeEvents);
1845                 }
1846             
1847                 if ((nativeEvents & receivedEvents) != 0) {
1848                     ThreadPool.QueueUserWorkItem(callReceiveEvents, nativeEvents);
1849                 }
1850             }
1851
1852             
1853             private void CallErrorEvents(object state) {
1854                 int errors = (int) state;
1855                 SerialStream stream = (SerialStream) streamWeakReference.Target;
1856                 if (stream == null)
1857                     return;
1858                 
1859                 if (stream.ErrorReceived != null) {
1860                     if ((errors & (int) SerialError.TXFull) != 0)
1861                         stream.ErrorReceived(stream, new SerialErrorReceivedEventArgs(SerialError.TXFull));
1862                 
1863                     if ((errors & (int) SerialError.RXOver) != 0)
1864                         stream.ErrorReceived(stream, new SerialErrorReceivedEventArgs(SerialError.RXOver));
1865                 
1866                     if ((errors & (int) SerialError.Overrun) != 0)
1867                         stream.ErrorReceived(stream, new SerialErrorReceivedEventArgs(SerialError.Overrun));
1868                 
1869                     if ((errors & (int) SerialError.RXParity) != 0)
1870                         stream.ErrorReceived(stream, new SerialErrorReceivedEventArgs(SerialError.RXParity));
1871                 
1872                     if ((errors & (int) SerialError.Frame) != 0)
1873                         stream.ErrorReceived(stream, new SerialErrorReceivedEventArgs(SerialError.Frame));
1874                 }
1875                 
1876                 stream = null;
1877             }
1878
1879             private void CallReceiveEvents(object state) {
1880                 int nativeEvents = (int) state;
1881                 SerialStream stream = (SerialStream) streamWeakReference.Target;
1882                 if (stream == null)
1883                     return;
1884                 
1885                 if (stream.DataReceived != null) {
1886                     if ((nativeEvents & (int) SerialData.Chars) != 0)
1887                         stream.DataReceived(stream, new SerialDataReceivedEventArgs(SerialData.Chars));
1888                     if ((nativeEvents & (int) SerialData.Eof) != 0)
1889                         stream.DataReceived(stream, new SerialDataReceivedEventArgs(SerialData.Eof));
1890                 }
1891                 
1892                 stream = null;
1893             }
1894
1895             private void CallPinEvents(object state) {
1896                 int nativeEvents = (int) state;
1897                 
1898                 SerialStream stream = (SerialStream) streamWeakReference.Target;
1899                 if (stream == null)
1900                     return;
1901                 
1902                 if (stream.PinChanged != null) {
1903                     if ((nativeEvents & (int) SerialPinChange.CtsChanged) != 0)
1904                         stream.PinChanged(stream, new SerialPinChangedEventArgs(SerialPinChange.CtsChanged));
1905                 
1906                     if ((nativeEvents & (int) SerialPinChange.DsrChanged) != 0)
1907                         stream.PinChanged(stream, new SerialPinChangedEventArgs(SerialPinChange.DsrChanged));
1908                 
1909                     if ((nativeEvents & (int) SerialPinChange.CDChanged) != 0)
1910                         stream.PinChanged(stream, new SerialPinChangedEventArgs(SerialPinChange.CDChanged));
1911                 
1912                     if ((nativeEvents & (int) SerialPinChange.Ring) != 0)
1913                         stream.PinChanged(stream, new SerialPinChangedEventArgs(SerialPinChange.Ring));
1914                 
1915                     if ((nativeEvents & (int) SerialPinChange.Break) != 0)
1916                         stream.PinChanged(stream, new SerialPinChangedEventArgs(SerialPinChange.Break));
1917                 }
1918                 
1919                 stream = null;
1920             }
1921             
1922         }
1923
1924         
1925         // This is an internal object implementing IAsyncResult with fields
1926         // for all of the relevant data necessary to complete the IO operation.
1927         // This is used by AsyncFSCallback and all async methods.
1928         unsafe internal sealed class SerialStreamAsyncResult : IAsyncResult
1929         {
1930             // User code callback
1931             internal AsyncCallback _userCallback;
1932
1933             internal Object _userStateObject;
1934
1935             internal bool _isWrite;     // Whether this is a read or a write
1936             internal bool _isComplete;
1937             internal bool _completedSynchronously;  // Which thread called callback
1938
1939             internal ManualResetEvent _waitHandle;
1940             internal int _EndXxxCalled;   // Whether we've called EndXxx already.
1941             internal int _numBytes;     // number of bytes read OR written
1942             internal int _errorCode;
1943             internal NativeOverlapped* _overlapped;
1944
1945             public Object AsyncState
1946             {
1947                 get { return _userStateObject; }
1948             }
1949
1950             public bool IsCompleted
1951             {
1952                 get { return _isComplete; }
1953             }
1954
1955             public WaitHandle AsyncWaitHandle
1956             {
1957                 get { 
1958                     /*
1959                       // Consider uncommenting this someday soon - the EventHandle 
1960                       // in the Overlapped struct is really useless half of the 
1961                       // time today since the OS doesn't signal it.  If users call
1962                       // EndXxx after the OS call happened to complete, there's no
1963                       // reason to create a synchronization primitive here.  Fixing
1964                       // this will save us some perf, assuming we can correctly
1965                       // initialize the ManualResetEvent. 
1966                     if (_waitHandle == null) {
1967                         ManualResetEvent mre = new ManualResetEvent(false);
1968                         if (_overlapped != null && _overlapped->EventHandle != IntPtr.Zero)
1969                             mre.Handle = _overlapped->EventHandle;
1970                         if (_isComplete)
1971                             mre.Set();
1972                         _waitHandle = mre;
1973                     }
1974                     */
1975                     return _waitHandle;
1976                 }
1977             }
1978
1979             // Returns true iff the user callback was called by the thread that
1980             // called BeginRead or BeginWrite.  If we use an async delegate or
1981             // threadpool thread internally, this will be false.  This is used
1982             // by code to determine whether a successive call to BeginRead needs
1983             // to be done on their main thread or in their callback to avoid a
1984             // stack overflow on many reads or writes.
1985             public bool CompletedSynchronously
1986             {
1987                 get { return _completedSynchronously; }
1988             }
1989         }
1990     }
1991 }