SerialStream.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / whidbey / NetFXspW7 / ndp / fx / src / Sys / System / IO / Ports / SerialStream.cs / 1 / SerialStream.cs

                            // ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 
/*==============================================================================
** 
** Class: SerialStream 
**
** Purpose: Class for enabling low-level [....] and async control over a serial 
**        : communications resource.
**
** Date: August, 2002
** 
=============================================================================*/
 
using System; 
using System.IO;
using System.Text; 
using System.ComponentModel;
using System.Resources;
using System.Runtime;
using System.Security; 
using System.Security.Permissions;
using System.Runtime.InteropServices; 
using System.Diagnostics; 
using System.Collections;
using Microsoft.Win32; 
using Microsoft.Win32.SafeHandles;
using System.Threading;
using System.Runtime.Remoting.Messaging;
using System.Runtime.CompilerServices; 
using System.Globalization;
using System.Runtime.Versioning; 
 
// Notes about the SerialStream:
//  * The stream is always opened via the SerialStream constructor. 
//  * The handleProtector guarantees ownership of the file handle, so that it may not be
//  * unnaturally closed by another process or thread.  Thus, since all properties are available
//  * only when the object exists, the object's properties can be queried only when the SerialStream
//  * object is instantiated (i.e. "open"). 
//  * Handles to serial communications resources here always:
//  * 1) own the handle 
//  * 2) are opened for asynchronous operation 
//  * 3) set access at the level of FileAccess.ReadWrite
//  * 4) Allow for reading AND writing 
//  * 5) Disallow seeking, since they encapsulate a file of type FILE_TYPE_CHAR

namespace System.IO.Ports
{ 
    internal sealed class SerialStream : Stream
    { 
        const int errorEvents = (int) (SerialError.Frame | SerialError.Overrun | 
                                 SerialError.RXOver | SerialError.RXParity | SerialError.TXFull);
        const int receivedEvents = (int) (SerialData.Chars | SerialData.Eof); 
        const int pinChangedEvents = (int) (SerialPinChange.Break | SerialPinChange.CDChanged | SerialPinChange.CtsChanged |
                                      SerialPinChange.Ring | SerialPinChange.DsrChanged);

        const int infiniteTimeoutConst = -2; 

        // members supporting properties exposed to SerialPort 
        private string portName; 
        private byte parityReplace = (byte) '?';
        private bool inBreak = false;               // port is initially in non-break state 
        private bool isAsync = true;
        private Handshake handshake;
        private bool rtsEnable = false;
 
        // The internal C# representations of Win32 structures necessary for communication
        // hold most of the internal "fields" maintaining information about the port. 
        private UnsafeNativeMethods.DCB dcb; 
        private UnsafeNativeMethods.COMMTIMEOUTS commTimeouts;
        private UnsafeNativeMethods.COMSTAT comStat; 
        private UnsafeNativeMethods.COMMPROP commProp;

        // internal-use members
        // private const long dsrTimeout = 0L; -- Not used anymore. 
        private const int maxDataBits = 8;
        private const int minDataBits = 5; 
        internal SafeFileHandle _handle = null; 
        internal EventLoopRunner eventRunner;
 
        private byte[] tempBuf;                 // used to avoid multiple array allocations in ReadByte()

        // called whenever any async i/o operation completes.
        private unsafe static readonly IOCompletionCallback IOCallback = new IOCompletionCallback(SerialStream.AsyncFSCallback); 

        // three different events, also wrapped by SerialPort. 
        internal event SerialDataReceivedEventHandler   DataReceived;      // called when one character is received. 
        internal event SerialPinChangedEventHandler PinChanged;    // called when any of the pin/ring-related triggers occurs
        internal event SerialErrorReceivedEventHandler      ErrorReceived;         // called when any runtime error occurs on the port (frame, overrun, parity, etc.) 


        // ----SECTION: inherited properties from Stream class ------------*
 
        // These six properites are required for SerialStream to inherit from the abstract Stream class.
        // Note four of them are always true or false, and two of them throw exceptions, so these 
        // are not usefully queried by applications which know they have a SerialStream, etc... 
        public override bool CanRead
        { 
            get { return (_handle != null); }
        }

        public override bool CanSeek 
        {
            get { return false; } 
        } 

        public override bool CanTimeout { 
            get { return (_handle != null); }
        }

        public override bool CanWrite 
        {
            get { return (_handle != null); } 
        } 

        public override long Length 
        {
            get { throw new NotSupportedException(SR.GetString(SR.NotSupported_UnseekableStream)); }
        }
 

        public override long Position 
        { 
            get { throw new NotSupportedException(SR.GetString(SR.NotSupported_UnseekableStream)); }
            set { throw new NotSupportedException(SR.GetString(SR.NotSupported_UnseekableStream)); } 
        }

        // ----- new get-set properties -----------------*
 
        // Standard port properties, also called from SerialPort
        // BaudRate may not be settable to an arbitrary integer between dwMinBaud and dwMaxBaud, 
        // and is limited only by the serial driver.  Typically about twelve values such 
        // as Winbase.h's CBR_110 through CBR_256000 are used.
        internal int BaudRate 
        {
            //get { return (int) dcb.BaudRate; }
            set
            { 
                if (value <= 0 || (value > commProp.dwMaxBaud && commProp.dwMaxBaud > 0))
                { 
                    // if no upper bound on baud rate imposed by serial driver, note that argument must be positive 
                    if (commProp.dwMaxBaud == 0)
                    { 
                        throw new ArgumentOutOfRangeException("baudRate",
                            SR.GetString(SR.ArgumentOutOfRange_NeedPosNum));
                    }
                    else 
                    {
                        // otherwise, we can present the bounds on the baud rate for this driver 
                        throw new ArgumentOutOfRangeException("baudRate", 
                            SR.GetString(SR.ArgumentOutOfRange_Bounds_Lower_Upper, 0, commProp.dwMaxBaud));
                    } 
                }
                // Set only if it's different.  Rollback to previous values if setting fails.
                //  This pattern occurs through most of the other properties in this class.
                if(value != dcb.BaudRate) 
                {
                    int baudRateOld = (int) dcb.BaudRate; 
                    dcb.BaudRate = (uint) value; 

                    if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false) 
                    {
                        dcb.BaudRate = (uint) baudRateOld;
                        InternalResources.WinIOError();
                    } 
                }
            } 
        } 

        public bool BreakState { 
            get { return inBreak; }
            set {
                if (value) {
                    if (UnsafeNativeMethods.SetCommBreak(_handle) == false) 
                        InternalResources.WinIOError();
                    inBreak = true; 
                } 
                else {
                    if (UnsafeNativeMethods.ClearCommBreak(_handle) == false) 
                        InternalResources.WinIOError();
                    inBreak = false;
                }
            } 
        }
 
        internal int DataBits 
        {
            //get  { return (int) dcb.ByteSize; } 
            set
            {
                Debug.Assert(!(value < minDataBits || value > maxDataBits), "An invalid value was passed to DataBits");
                if (value != dcb.ByteSize) 
                {
                    byte byteSizeOld = dcb.ByteSize; 
                    dcb.ByteSize = (byte) value; 

                    if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false) 
                    {
                        dcb.ByteSize = byteSizeOld;
                        InternalResources.WinIOError();
                    } 
                }
            } 
        } 

 
        internal bool DiscardNull
        {
            //get {   return (GetDcbFlag(NativeMethods.FNULL) == 1);}
            set 
            {
                int fNullFlag = GetDcbFlag(NativeMethods.FNULL); 
                if(value == true && fNullFlag == 0 || value == false && fNullFlag == 1) 
                {
                    int fNullOld = fNullFlag; 
                    SetDcbFlag(NativeMethods.FNULL, value ? 1 : 0);

                    if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false)
                    { 
                        SetDcbFlag(NativeMethods.FNULL, fNullOld);
                        InternalResources.WinIOError(); 
                    } 
                }
            } 
        }

        internal bool DtrEnable
        { 
            get {
                int fDtrControl = GetDcbFlag(NativeMethods.FDTRCONTROL); 
 
                return (fDtrControl == NativeMethods.DTR_CONTROL_ENABLE);
            } 
            set
            {
                // first set the FDTRCONTROL field in the DCB struct
                int fDtrControlOld = GetDcbFlag(NativeMethods.FDTRCONTROL); 

                SetDcbFlag(NativeMethods.FDTRCONTROL, value ? NativeMethods.DTR_CONTROL_ENABLE : NativeMethods.DTR_CONTROL_DISABLE); 
                if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false) 
                {
                    SetDcbFlag(NativeMethods.FDTRCONTROL, fDtrControlOld); 
                    InternalResources.WinIOError();
                }

                // then set the actual pin 
                if (!UnsafeNativeMethods.EscapeCommFunction(_handle, value ? NativeMethods.SETDTR : NativeMethods.CLRDTR))
                    InternalResources.WinIOError(); 
 
            }
        } 

        internal Handshake Handshake
        {
            //get  { return handshake; } 
            set
            { 
 
                Debug.Assert(!(value < System.IO.Ports.Handshake.None || value > System.IO.Ports.Handshake.RequestToSendXOnXOff),
                    "An invalid value was passed to Handshake"); 

                if(value != handshake)
                {
                    // in the DCB, handshake affects the fRtsControl, fOutxCtsFlow, and fInX, fOutX fields, 
                    // so we must save everything in that closure before making any changes.
                    Handshake handshakeOld = handshake; 
                    int fInOutXOld = GetDcbFlag(NativeMethods.FINX); 
                    int fOutxCtsFlowOld = GetDcbFlag(NativeMethods.FOUTXCTSFLOW);
                    int fRtsControlOld = GetDcbFlag(NativeMethods.FRTSCONTROL); 

                    handshake = value;
                    int fInXOutXFlag = (handshake == Handshake.XOnXOff || handshake == Handshake.RequestToSendXOnXOff) ? 1 : 0;
                    SetDcbFlag(NativeMethods.FINX, fInXOutXFlag); 
                    SetDcbFlag(NativeMethods.FOUTX, fInXOutXFlag);
 
                    SetDcbFlag(NativeMethods.FOUTXCTSFLOW, (handshake == Handshake.RequestToSend || 
                        handshake == Handshake.RequestToSendXOnXOff) ? 1 : 0);
 
                    if ((handshake == Handshake.RequestToSend ||
                        handshake == Handshake.RequestToSendXOnXOff))
                    {
                        SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_HANDSHAKE); 
                    }
                    else if (rtsEnable) 
                    { 
                        SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_ENABLE);
                    } 
                    else {
                        SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_DISABLE);
                    }
 
                    if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false)
                    { 
                        handshake = handshakeOld; 
                        SetDcbFlag(NativeMethods.FINX, fInOutXOld);
                        SetDcbFlag(NativeMethods.FOUTX, fInOutXOld); 
                        SetDcbFlag(NativeMethods.FOUTXCTSFLOW, fOutxCtsFlowOld);
                        SetDcbFlag(NativeMethods.FRTSCONTROL, fRtsControlOld);
                        InternalResources.WinIOError();
                    } 

                } 
            } 
        }
 
        internal bool IsOpen {
            get {
                return _handle != null && !eventRunner.ShutdownLoop;
            } 
        }
 
        internal Parity Parity 
        {
            //get     {   return (Parity) dcb.Parity;     } 
            set
            {
                Debug.Assert(!(value < Parity.None || value > Parity.Space), "An invalid value was passed to Parity");
 
                if((byte) value != dcb.Parity)
                { 
                    byte parityOld = dcb.Parity; 

                    // in the DCB structure, the parity setting also potentially effects: 
                    // fParity, fErrorChar, ErrorChar
                    // so these must be saved as well.
                    int fParityOld = GetDcbFlag(NativeMethods.FPARITY);
                    byte ErrorCharOld = dcb.ErrorChar; 
                    int fErrorCharOld = GetDcbFlag(NativeMethods.FERRORCHAR);
                    dcb.Parity = (byte) value; 
 
                    int parityFlag = (dcb.Parity == (byte) Parity.None) ? 0 : 1;
                    SetDcbFlag(NativeMethods.FPARITY, parityFlag); 
                    if (parityFlag == 1)
                    {
                        SetDcbFlag(NativeMethods.FERRORCHAR, (parityReplace != '\0') ? 1 : 0);
                        dcb.ErrorChar = parityReplace; 
                    }
                    else 
                    { 
                        SetDcbFlag(NativeMethods.FERRORCHAR, 0);
                        dcb.ErrorChar = (byte) '\0'; 
                    }
                    if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false)
                    {
                        dcb.Parity = parityOld; 
                        SetDcbFlag(NativeMethods.FPARITY, fParityOld);
 
                        dcb.ErrorChar = ErrorCharOld; 
                        SetDcbFlag(NativeMethods.FERRORCHAR, fErrorCharOld);
 
                        InternalResources.WinIOError();
                    }
                }
            } 
        }
 
        // ParityReplace is the eight-bit character which replaces any bytes which 
        // ParityReplace affects the equivalent field in the DCB structure: ErrorChar, and
        // the DCB flag fErrorChar. 
        internal byte ParityReplace
        {
            //get {   return parityReplace; }
            set 
            {
                if(value != parityReplace) 
                { 
                    byte parityReplaceOld = parityReplace;
                    byte errorCharOld = dcb.ErrorChar; 
                    int fErrorCharOld = GetDcbFlag(NativeMethods.FERRORCHAR);

                    parityReplace = value;
                    if (GetDcbFlag(NativeMethods.FPARITY) == 1) 
                    {
                        SetDcbFlag(NativeMethods.FERRORCHAR, (parityReplace != '\0')? 1 : 0); 
                        dcb.ErrorChar = parityReplace; 
                    }
                    else 
                    {
                        SetDcbFlag(NativeMethods.FERRORCHAR, 0);
                        dcb.ErrorChar = (byte) '\0';
                    } 

 
                    if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false) 
                    {
                        parityReplace = parityReplaceOld; 
                        SetDcbFlag(NativeMethods.FERRORCHAR, fErrorCharOld);
                        dcb.ErrorChar = errorCharOld;
                        InternalResources.WinIOError();
                    } 
                }
            } 
        } 

        // Timeouts are considered to be TOTAL time for the Read/Write operation and to be in milliseconds. 
        // Timeouts are translated into DCB structure as follows:
        // Desired timeout      =>  ReadTotalTimeoutConstant    ReadTotalTimeoutMultiplier  ReadIntervalTimeout
        //  0                                   0                           0               MAXDWORD
        //  0 < n < infinity                    n                       MAXDWORD            MAXDWORD 
        // infinity                             infiniteTimeoutConst    MAXDWORD            MAXDWORD
        // 
        // rationale for "infinity": There does not exist in the COMMTIMEOUTS structure a way to 
        // *wait indefinitely for any byte, return when found*.  Instead, if we set ReadTimeout
        // to infinity, SerialStream's EndRead loops if infiniteTimeoutConst mills have elapsed 
        // without a byte received.  Note that this is approximately 24 days, so essentially
        // most practical purposes effectively equate 24 days with an infinite amount of time
        // on a serial port connection.
        public override int ReadTimeout 
        {
            get 
            { 
                int constant = commTimeouts.ReadTotalTimeoutConstant;
 
                if (constant == infiniteTimeoutConst) return SerialPort.InfiniteTimeout;
                else return constant;
            }
            set 
            {
                if (value < 0 && value != SerialPort.InfiniteTimeout) 
                    throw new ArgumentOutOfRangeException("ReadTimeout", SR.GetString(SR.ArgumentOutOfRange_Timeout)); 
                if (_handle == null) InternalResources.FileNotOpen();
 
                int oldReadConstant = commTimeouts.ReadTotalTimeoutConstant;
                int oldReadInterval = commTimeouts.ReadIntervalTimeout;
                int oldReadMultipler = commTimeouts.ReadTotalTimeoutMultiplier;
 
                // NOTE: this logic should match what is in the constructor
                if (value == 0) { 
                    commTimeouts.ReadTotalTimeoutConstant   = 0; 
                    commTimeouts.ReadTotalTimeoutMultiplier = 0;
                    commTimeouts.ReadIntervalTimeout        = NativeMethods.MAXDWORD; 
                } else if (value == SerialPort.InfiniteTimeout) {
                    // SetCommTimeouts doesn't like a value of -1 for some reason, so
                    // we'll use -2(infiniteTimeoutConst) to represent infinite.
                    commTimeouts.ReadTotalTimeoutConstant   = infiniteTimeoutConst; 
                    commTimeouts.ReadTotalTimeoutMultiplier = NativeMethods.MAXDWORD;
                    commTimeouts.ReadIntervalTimeout        = NativeMethods.MAXDWORD; 
                } else { 
                    commTimeouts.ReadTotalTimeoutConstant   = value;
                    commTimeouts.ReadTotalTimeoutMultiplier = NativeMethods.MAXDWORD; 
                    commTimeouts.ReadIntervalTimeout        = NativeMethods.MAXDWORD;
                }

                if (UnsafeNativeMethods.SetCommTimeouts(_handle, ref commTimeouts) == false) 
                {
                    commTimeouts.ReadTotalTimeoutConstant = oldReadConstant; 
                    commTimeouts.ReadTotalTimeoutMultiplier = oldReadMultipler; 
                    commTimeouts.ReadIntervalTimeout = oldReadInterval;
                    InternalResources.WinIOError(); 
                }
            }
        }
 
        internal bool RtsEnable
        { 
            get { 
                int fRtsControl = GetDcbFlag(NativeMethods.FRTSCONTROL);
                if (fRtsControl == NativeMethods.RTS_CONTROL_HANDSHAKE) 
                    throw new InvalidOperationException(SR.GetString(SR.CantSetRtsWithHandshaking));

                return (fRtsControl == NativeMethods.RTS_CONTROL_ENABLE);
            } 
            set
            { 
                if ((handshake == Handshake.RequestToSend || handshake == Handshake.RequestToSendXOnXOff)) 
                    throw new InvalidOperationException(SR.GetString(SR.CantSetRtsWithHandshaking));
 
                if (value != rtsEnable) {
                    int fRtsControlOld = GetDcbFlag(NativeMethods.FRTSCONTROL);

                    rtsEnable = value; 
                    if(value)
                        SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_ENABLE); 
                    else 
                        SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_DISABLE);
 
                    if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false)
                    {
                        SetDcbFlag(NativeMethods.FRTSCONTROL, fRtsControlOld);
                        // set it back to the old value on a failure 
                        rtsEnable = !rtsEnable;
                        InternalResources.WinIOError(); 
                    } 

                    if (!UnsafeNativeMethods.EscapeCommFunction(_handle, value ? NativeMethods.SETRTS : NativeMethods.CLRRTS)) 
                        InternalResources.WinIOError();
                }
            }
        } 

        // StopBits represented in C# as StopBits enum type and in Win32 as an integer 1, 2, or 3. 
        internal StopBits StopBits 
        {
            /*get 
            {
                switch(dcb.StopBits)
                {
                    case NativeMethods.ONESTOPBIT: 
                        return StopBits.One;
                    case NativeMethods.ONE5STOPBITS: 
                        return StopBits.OnePointFive; 
                    case NativeMethods.TWOSTOPBITS:
                        return StopBits.Two; 
                    default:
                        Debug.Assert(true, "Invalid Stopbits value " + dcb.StopBits);
                        return StopBits.One;
 
                }
            } 
            */ 
            set
            { 
                Debug.Assert(!(value < StopBits.One || value > StopBits.OnePointFive), "An invalid value was passed to StopBits");

                byte nativeValue = 0;
                if (value == StopBits.One) nativeValue = (byte) NativeMethods.ONESTOPBIT; 
                else if (value == StopBits.OnePointFive) nativeValue = (byte) NativeMethods.ONE5STOPBITS;
                else nativeValue = (byte) NativeMethods.TWOSTOPBITS; 
 
                if(nativeValue != dcb.StopBits)
                { 
                    byte stopBitsOld = dcb.StopBits;
                    dcb.StopBits = nativeValue;

                    if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false) 
                    {
                        dcb.StopBits = stopBitsOld; 
                        InternalResources.WinIOError(); 
                    }
                } 
            }
        }

        // note: WriteTimeout must be either SerialPort.InfiniteTimeout or POSITIVE. 
        // a timeout of zero implies that every Write call throws an exception.
        public override int WriteTimeout 
        { 
            get
            { 
                int timeout = commTimeouts.WriteTotalTimeoutConstant;
                return (timeout == 0) ? SerialPort.InfiniteTimeout : timeout;
            }
            set 
            {
                if (value <= 0 && value != SerialPort.InfiniteTimeout) 
                    throw new ArgumentOutOfRangeException("WriteTimeout", SR.GetString(SR.ArgumentOutOfRange_WriteTimeout)); 
                if (_handle == null) InternalResources.FileNotOpen();
 
                int oldWriteConstant = commTimeouts.WriteTotalTimeoutConstant;
                commTimeouts.WriteTotalTimeoutConstant = ((value == SerialPort.InfiniteTimeout) ? 0 : value);

                if (UnsafeNativeMethods.SetCommTimeouts(_handle, ref commTimeouts) == false) 
                {
                    commTimeouts.WriteTotalTimeoutConstant = oldWriteConstant; 
                    InternalResources.WinIOError(); 
                }
            } 
        }


        // CDHolding, CtsHolding, DsrHolding query the current state of each of the carrier, the CTS pin, 
        // and the DSR pin, respectively. Read-only.
        // All will throw exceptions if the port is not open. 
        internal bool CDHolding 
        {
            get 
            {
                int pinStatus = 0;
                if (UnsafeNativeMethods.GetCommModemStatus(_handle, ref pinStatus) == false)
                    InternalResources.WinIOError(); 

                return (NativeMethods.MS_RLSD_ON & pinStatus) != 0; 
            } 
        }
 

        internal bool CtsHolding
        {
            get 
            {
                int pinStatus = 0; 
                if (UnsafeNativeMethods.GetCommModemStatus(_handle, ref pinStatus) == false) 
                    InternalResources.WinIOError();
                return (NativeMethods.MS_CTS_ON & pinStatus) != 0; 
            }

        }
 
        internal bool DsrHolding
        { 
            get 
            {
                int pinStatus = 0; 
                if (UnsafeNativeMethods.GetCommModemStatus(_handle, ref pinStatus) == false)
                    InternalResources.WinIOError();

                return (NativeMethods.MS_DSR_ON & pinStatus) != 0; 
            }
        } 
 

        // Fills comStat structure from an unmanaged function 
        // to determine the number of bytes waiting in the serial driver's internal receive buffer.
        internal int BytesToRead {
            get
            { 
                int errorCode = 0; // "ref" arguments need to have values, as opposed to "out" arguments
                if (UnsafeNativeMethods.ClearCommError(_handle, ref errorCode, ref comStat)  == false) 
                { 
                    InternalResources.WinIOError();
                } 
                return (int) comStat.cbInQue;
            }
        }
 
        // Fills comStat structure from an unmanaged function
        // to determine the number of bytes waiting in the serial driver's internal transmit buffer. 
        internal int BytesToWrite { 
            get
            { 
                int errorCode = 0; // "ref" arguments need to be set before method invocation, as opposed to "out" arguments
                if (UnsafeNativeMethods.ClearCommError(_handle, ref errorCode, ref comStat)  == false)
                    InternalResources.WinIOError();
                return (int) comStat.cbOutQue; 

            } 
        } 

        // -----------SECTION: constructor --------------------------* 

        // this method is used by SerialPort upon SerialStream's creation
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)] 
        internal SerialStream(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits, int readTimeout, int writeTimeout, Handshake handshake,
            bool dtrEnable, bool rtsEnable, bool discardNull, byte parityReplace) 
        { 

            int flags = UnsafeNativeMethods.FILE_FLAG_OVERLAPPED; 
            // disable async on win9x
            if (Environment.OSVersion.Platform == PlatformID.Win32Windows) {
                flags = UnsafeNativeMethods.FILE_ATTRIBUTE_NORMAL;
                isAsync = false; 
            }
 
            if ((portName == null) || !portName.StartsWith("COM", StringComparison.OrdinalIgnoreCase)) 
                throw new ArgumentException(SR.GetString(SR.Arg_InvalidSerialPort), "portName");
 
            //Error checking done in SerialPort.

            SafeFileHandle tempHandle = UnsafeNativeMethods.CreateFile("\\\\.\\" + portName,
                NativeMethods.GENERIC_READ | NativeMethods.GENERIC_WRITE, 
                0,    // comm devices must be opened w/exclusive-access
                IntPtr.Zero, // no security attributes 
                UnsafeNativeMethods.OPEN_EXISTING, // comm devices must use OPEN_EXISTING 
                flags,
                IntPtr.Zero  // hTemplate must be NULL for comm devices 
                );

            if (tempHandle.IsInvalid)
            { 
                InternalResources.WinIOError(portName);
            } 
 
            try {
                int fileType = UnsafeNativeMethods.GetFileType(tempHandle); 

                // Allowing FILE_TYPE_UNKNOWN for legitimate serial device such as USB to serial adapter device
                if ((fileType != UnsafeNativeMethods.FILE_TYPE_CHAR) && (fileType != UnsafeNativeMethods.FILE_TYPE_UNKNOWN))
                    throw new ArgumentException(SR.GetString(SR.Arg_InvalidSerialPort), "portName"); 

                _handle = tempHandle; 
 
                // set properties of the stream that exist as members in SerialStream
                this.portName = portName; 
                this.handshake = handshake;
                this.parityReplace = parityReplace;

                tempBuf = new byte[1];          // used in ReadByte() 

                // Fill COMMPROPERTIES struct, which has our maximum allowed baud rate. 
                // Call a serial specific API such as GetCommModemStatus which would fail 
                // in case the device is not a legitimate serial device. For instance,
                // some illegal FILE_TYPE_UNKNOWN device (or) "LPT1" on Win9x 
                // trying to pass for serial will be caught here. GetCommProperties works
                // fine for "LPT1" on Win9x, so that alone can't be relied here to
                // detect non serial devices.
 
                commProp = new UnsafeNativeMethods.COMMPROP();
                int pinStatus = 0; 
 
                if (!UnsafeNativeMethods.GetCommProperties(_handle, ref commProp)
                    || !UnsafeNativeMethods.GetCommModemStatus(_handle, ref pinStatus)) 
                {
                    // If the portName they have passed in is a FILE_TYPE_CHAR but not a serial port,
                    // for example "LPT1", this API will fail.  For this reason we handle the error message specially.
                    int errorCode = Marshal.GetLastWin32Error(); 
                    if ((errorCode == NativeMethods.ERROR_INVALID_PARAMETER) || (errorCode == NativeMethods.ERROR_INVALID_HANDLE))
                        throw new ArgumentException(SR.GetString(SR.Arg_InvalidSerialPortExtended), "portName"); 
                    else 
                        InternalResources.WinIOError(errorCode, string.Empty);
                } 
                if (commProp.dwMaxBaud != 0 && baudRate > commProp.dwMaxBaud)
                    throw new ArgumentOutOfRangeException("baudRate", SR.GetString(SR.Max_Baud, commProp.dwMaxBaud));

 
                comStat = new UnsafeNativeMethods.COMSTAT();
                // create internal DCB structure, initialize according to Platform SDK 
                // standard: ms-help://MS.MSNDNQTR.2002APR.1003/hardware/commun_965u.htm 
                dcb = new UnsafeNativeMethods.DCB();
 
                // set constant properties of the DCB
                InitializeDCB(baudRate, parity, dataBits, stopBits, discardNull);

                this.DtrEnable = dtrEnable; 

                // query and cache the initial RtsEnable value 
                // so that set_RtsEnable can do the (value != rtsEnable) optimization 
                this.rtsEnable = (GetDcbFlag(NativeMethods.FRTSCONTROL) == NativeMethods.RTS_CONTROL_ENABLE);
 
                // now set this.RtsEnable to the specified value.
                // Handshake takes precedence, this will be a nop if
                // handshake is either RequestToSend or RequestToSendXOnXOff
                if ((handshake != Handshake.RequestToSend && handshake != Handshake.RequestToSendXOnXOff)) 
                    this.RtsEnable = rtsEnable;
 
                // NOTE: this logic should match what is in the ReadTimeout property 
                if (readTimeout == 0) {
                    commTimeouts.ReadTotalTimeoutConstant   = 0; 
                    commTimeouts.ReadTotalTimeoutMultiplier = 0;
                    commTimeouts.ReadIntervalTimeout        = NativeMethods.MAXDWORD;
                } else if (readTimeout == SerialPort.InfiniteTimeout) {
                    // SetCommTimeouts doesn't like a value of -1 for some reason, so 
                    // we'll use -2(infiniteTimeoutConst) to represent infinite.
                    commTimeouts.ReadTotalTimeoutConstant   = infiniteTimeoutConst; 
                    commTimeouts.ReadTotalTimeoutMultiplier = NativeMethods.MAXDWORD; 
                    commTimeouts.ReadIntervalTimeout        = NativeMethods.MAXDWORD;
                } else { 
                    commTimeouts.ReadTotalTimeoutConstant   = readTimeout;
                    commTimeouts.ReadTotalTimeoutMultiplier = NativeMethods.MAXDWORD;
                    commTimeouts.ReadIntervalTimeout        = NativeMethods.MAXDWORD;
                } 

                commTimeouts.WriteTotalTimeoutMultiplier    = 0; 
                commTimeouts.WriteTotalTimeoutConstant      = ((writeTimeout == SerialPort.InfiniteTimeout) ? 0 : writeTimeout); 

                // set unmanaged timeout structure 
                if (UnsafeNativeMethods.SetCommTimeouts(_handle, ref commTimeouts) == false)
                {
                    InternalResources.WinIOError();
                } 

                if (isAsync) { 
                    if (!ThreadPool.BindHandle(_handle)) 
                    {
                        throw new IOException(SR.GetString(SR.IO_BindHandleFailed)); 
                    }
                }

                // monitor all events except TXEMPTY 
                UnsafeNativeMethods.SetCommMask(_handle, NativeMethods.ALL_EVENTS);
 
                // prep. for starting event cycle. 
                eventRunner = new EventLoopRunner(this);
                Thread eventLoopThread = new Thread(new ThreadStart(eventRunner.WaitForCommEvent)); 
                eventLoopThread.IsBackground = true;
                eventLoopThread.Start();

            } 
            catch  {
                // if there are any exceptions after the call to CreateFile, we need to be sure to close the 
                // handle before we let them continue up. 
                tempHandle.Close();
                _handle = null; 
                throw;
            }
        }
 
        ~SerialStream()
        { 
            Dispose(false); 
        }
 
        protected override void Dispose(bool disposing)
        {
            // Signal the other side that we're closing.  Should do regardless of whether we've called
            // Close() or not Dispose() 
            if (_handle != null && !_handle.IsInvalid) {
                try { 
 
                    eventRunner.endEventLoop = true;
 
                    Thread.MemoryBarrier();

                    // turn off all events and signal WaitCommEvent
                    UnsafeNativeMethods.SetCommMask(_handle, 0); 
                    if(!UnsafeNativeMethods.EscapeCommFunction(_handle, NativeMethods.CLRDTR))
                    { 
                        // should not happen 
                        InternalResources.WinIOError();
                    } 

                    if (!_handle.IsClosed)
                        Flush();
 
                    eventRunner.waitCommEventWaitHandle.Set();
                    DiscardInBuffer(); 
                    DiscardOutBuffer(); 

                    if (disposing && eventRunner != null) { 
                        // now we need to wait for the event loop to tell us it's done.  Without this we could get into a ---- where the
                        // event loop kept the port open even after Dispose ended.
                        eventRunner.eventLoopEndedSignal.WaitOne();
                        eventRunner.eventLoopEndedSignal.Close(); 
                        eventRunner.waitCommEventWaitHandle.Close();
                    } 
                } 
                finally {
                    // If we are disposing synchronize closing with raising SerialPort events 
                    if (disposing) {
                        lock (this) {
                            _handle.Close();
                            _handle = null; 
                        }
                    } 
                    else { 
                        _handle.Close();
                        _handle = null; 
                    }
                    base.Dispose(disposing);
                }
 
            }
        } 
 
        // -----SECTION: all public methods ------------------*
 
        // User-accessible async read method.  Returns SerialStreamAsyncResult : IAsyncResult
        [HostProtection(ExternalThreading=true)]
        public override IAsyncResult BeginRead(byte[] array, int offset,int numBytes, AsyncCallback userCallback, object stateObject)
        { 
            if (array==null)
                throw new ArgumentNullException("array"); 
            if (offset < 0) 
                throw new ArgumentOutOfRangeException("offset", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired));
            if (numBytes < 0) 
                throw new ArgumentOutOfRangeException("numBytes", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired));
            if (array.Length - offset < numBytes)
                throw new ArgumentException(SR.GetString(SR.Argument_InvalidOffLen));
            if (_handle == null) InternalResources.FileNotOpen(); 

            int oldtimeout = ReadTimeout; 
            ReadTimeout = SerialPort.InfiniteTimeout; 
            IAsyncResult result;
            try { 
                if (!isAsync)
                    result = base.BeginRead(array, offset, numBytes, userCallback, stateObject);
                else
                    result = BeginReadCore(array, offset, numBytes, userCallback, stateObject); 

            } 
            finally { 
                ReadTimeout = oldtimeout;
            } 
            return result;
        }

        // User-accessible async write method.  Returns SerialStreamAsyncResult : IAsyncResult 
        // Throws an exception if port is in break state.
        [HostProtection(ExternalThreading=true)] 
        public override IAsyncResult BeginWrite(byte[] array, int offset, int numBytes, 
            AsyncCallback userCallback, object stateObject)
        { 
            if (inBreak)
                throw new InvalidOperationException(SR.GetString(SR.In_Break_State));
            if (array==null)
                throw new ArgumentNullException("array"); 
            if (offset < 0)
                throw new ArgumentOutOfRangeException("offset", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired)); 
            if (numBytes < 0) 
                throw new ArgumentOutOfRangeException("numBytes", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired));
            if (array.Length - offset < numBytes) 
                throw new ArgumentException(SR.GetString(SR.Argument_InvalidOffLen));
            if (_handle == null) InternalResources.FileNotOpen();

            int oldtimeout = WriteTimeout; 
            WriteTimeout = SerialPort.InfiniteTimeout;
            IAsyncResult result; 
            try { 
                if (!isAsync)
                    result = base.BeginWrite(array, offset, numBytes, userCallback, stateObject); 
                else
                    result = BeginWriteCore(array, offset, numBytes, userCallback, stateObject);
            }
            finally { 
                WriteTimeout = oldtimeout;
            } 
            return result; 
        }
 
        // Uses Win32 method to dump out the receive buffer; analagous to MSComm's "InBufferCount = 0"
        internal void DiscardInBuffer()
        {
 
            if (UnsafeNativeMethods.PurgeComm(_handle, NativeMethods.PURGE_RXCLEAR | NativeMethods.PURGE_RXABORT) == false)
                InternalResources.WinIOError(); 
        } 

        // Uses Win32 method to dump out the xmit buffer; analagous to MSComm's "OutBufferCount = 0" 
        internal void DiscardOutBuffer()
        {
            if (UnsafeNativeMethods.PurgeComm(_handle, NativeMethods.PURGE_TXCLEAR | NativeMethods.PURGE_TXABORT) == false)
                InternalResources.WinIOError(); 
        }
 
        // Async companion to BeginRead. 
        // Note, assumed IAsyncResult argument is of derived type SerialStreamAsyncResult,
        // and throws an exception if untrue. 
        public unsafe override int EndRead(IAsyncResult asyncResult)
        {
            if (!isAsync)
                return base.EndRead(asyncResult); 

            if (asyncResult==null) 
                throw new ArgumentNullException("asyncResult"); 

            SerialStreamAsyncResult afsar = asyncResult as SerialStreamAsyncResult; 
            if (afsar==null || afsar._isWrite)
                InternalResources.WrongAsyncResult();

            // This sidesteps race conditions, avoids memory corruption after freeing the 
            // NativeOverlapped class or GCHandle twice.
            if (1 == Interlocked.CompareExchange(ref afsar._EndXxxCalled, 1, 0)) 
                InternalResources.EndReadCalledTwice(); 

            bool failed = false; 

            // Obtain the WaitHandle, but don't use public property in case we
            // delay initialize the manual reset event in the future.
            WaitHandle wh = afsar._waitHandle; 
            if (wh != null)
            { 
                // We must block to ensure that AsyncFSCallback has completed, 
                // and we should close the WaitHandle in here.
                try { 
                    wh.WaitOne();
                    Debug.Assert(afsar._isComplete == true, "SerialStream::EndRead - AsyncFSCallback didn't set _isComplete to true!");

                    // InfiniteTimeout is not something native to the underlying serial device, 
                    // we specify the timeout to be a very large value (MAXWORD-1) to achieve
                    // an infinite timeout illusion. 
 
                    // I'm not sure what we can do here after an asyn operation with infinite
                    // timeout returns with no data. From a purist point of view we should 
                    // somehow restart the read operation but we are not in a position to do so
                    // (and frankly that may not necessarily be the right thing to do here)
                    // I think the best option in this (almost impossible to run into) situation
                    // is to throw some sort of IOException. 

                    if ((afsar._numBytes == 0) && (ReadTimeout == SerialPort.InfiniteTimeout) && (afsar._errorCode == 0)) 
                      failed = true; 
                }
                finally { 
                    wh.Close();
                }
            }
 
            // Free memory, GC handles.
            NativeOverlapped* overlappedPtr = afsar._overlapped; 
            if (overlappedPtr != null) 
                Overlapped.Free(overlappedPtr);
            afsar.UnpinBuffer(); 

            // Check for non-timeout errors during the read.
            if (afsar._errorCode != 0)
                InternalResources.WinIOError(afsar._errorCode, portName); 

            if (failed) 
                throw new IOException(SR.GetString(SR.IO_OperationAborted)); 

            return afsar._numBytes; 
        }

        // Async companion to BeginWrite.
        // Note, assumed IAsyncResult argument is of derived type SerialStreamAsyncResult, 
        // and throws an exception if untrue.
        // Also fails if called in port's break state. 
        public unsafe override void EndWrite(IAsyncResult asyncResult) { 
            if (!isAsync) {
                base.EndWrite(asyncResult); 
                return;
            }

            if (inBreak) 
                throw new InvalidOperationException(SR.GetString(SR.In_Break_State));
            if (asyncResult==null) 
                throw new ArgumentNullException("asyncResult"); 

            SerialStreamAsyncResult afsar = asyncResult as SerialStreamAsyncResult; 
            if (afsar==null || !afsar._isWrite)
                InternalResources.WrongAsyncResult();

            // This sidesteps race conditions, avoids memory corruption after freeing the 
            // NativeOverlapped class or GCHandle twice.
            if (1 == Interlocked.CompareExchange(ref afsar._EndXxxCalled, 1, 0)) 
                InternalResources.EndWriteCalledTwice(); 

            // Obtain the WaitHandle, but don't use public property in case we 
            // delay initialize the manual reset event in the future.
            WaitHandle wh = afsar._waitHandle;
            if (wh != null)
            { 
                // We must block to ensure that AsyncFSCallback has completed,
                // and we should close the WaitHandle in here. 
                try { 
                    wh.WaitOne();
                    Debug.Assert(afsar._isComplete == true, "SerialStream::EndWrite - AsyncFSCallback didn't set _isComplete to true!"); 
                }
                finally {
                    wh.Close();
                } 
            }
 
            // Free memory, GC handles. 
            NativeOverlapped* overlappedPtr = afsar._overlapped;
            if (overlappedPtr != null) 
                Overlapped.Free(overlappedPtr);
            afsar.UnpinBuffer();

            // Now check for any error during the write. 
            if (afsar._errorCode != 0)
                InternalResources.WinIOError(afsar._errorCode, portName); 
 
            // Number of bytes written is afsar._numBytes.
        } 

        // Flush dumps the contents of the serial driver's internal read and write buffers.
        // We actually expose the functionality for each, but fulfilling Stream's contract
        // requires a Flush() method.  Fails if handle closed. 
        // Note: Serial driver's write buffer is *already* attempting to write it, so we can only wait until it finishes.
        public override void Flush() 
        { 
            if (_handle == null) throw new ObjectDisposedException(SR.GetString(SR.Port_not_open));
            UnsafeNativeMethods.FlushFileBuffers(_handle); 
        }

        // Blocking read operation, returning the number of bytes read from the stream.
 
        public override int Read([In, Out] byte[] array, int offset, int count)
        { 
            return Read(array, offset, count, ReadTimeout); 
        }
 
        internal unsafe int Read([In, Out] byte[] array, int offset, int count, int timeout)
        {
            if (array==null)
                throw new ArgumentNullException("array", SR.GetString(SR.ArgumentNull_Buffer)); 
            if (offset < 0)
                throw new ArgumentOutOfRangeException("offset", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired)); 
            if (count < 0) 
                throw new ArgumentOutOfRangeException("count", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired));
            if (array.Length - offset < count) 
                throw new ArgumentException(SR.GetString(SR.Argument_InvalidOffLen));
            if (count == 0) return 0; // return immediately if no bytes requested; no need for overhead.

            Debug.Assert(timeout == SerialPort.InfiniteTimeout || timeout >= 0, "Serial Stream Read - called with timeout " + timeout); 

            // Check to see we have no handle-related error, since the port's always supposed to be open. 
            if (_handle == null) InternalResources.FileNotOpen(); 

            int numBytes = 0; 
            int hr;
            if (isAsync) {
                IAsyncResult result = BeginReadCore(array, offset, count, null, null);
                numBytes = EndRead(result); 
            }
            else { 
                numBytes = ReadFileNative(array, offset, count, null, out hr); 
                if (numBytes == -1) {
                    InternalResources.WinIOError(); 
                }
            }

            if (numBytes == 0) 
                throw new TimeoutException();
 
            return numBytes; 
        }
 
        public override int ReadByte()
        {
            return ReadByte(ReadTimeout);
        } 

        internal unsafe int ReadByte(int timeout) 
        { 
            if (_handle == null) InternalResources.FileNotOpen();
 
            int numBytes = 0;
            int hr;
            if (isAsync) {
                IAsyncResult result = BeginReadCore(tempBuf, 0, 1, null, null); 
                numBytes = EndRead(result);
            } 
            else { 
                numBytes = ReadFileNative(tempBuf, 0, 1, null, out hr);
                if (numBytes == -1) { 
                    InternalResources.WinIOError();
                }
            }
 
            if (numBytes == 0)
                throw new TimeoutException(); 
            else 
                return tempBuf[0];
        } 

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException(SR.GetString(SR.NotSupported_UnseekableStream)); 
        }
 
        public override void SetLength(long value) 
        {
            throw new NotSupportedException(SR.GetString(SR.NotSupported_UnseekableStream)); 
        }

        internal void SetBufferSizes(int readBufferSize, int writeBufferSize) {
            if (_handle == null) InternalResources.FileNotOpen(); 

            if (!UnsafeNativeMethods.SetupComm(_handle, readBufferSize, writeBufferSize)) 
                InternalResources.WinIOError(); 
        }
 
        public override void Write(byte[] array, int offset, int count)
        {
            Write(array, offset, count, WriteTimeout);
        } 

        internal unsafe void Write(byte[] array, int offset, int count, int timeout) 
        { 

            if (inBreak) 
                throw new InvalidOperationException(SR.GetString(SR.In_Break_State));
            if (array==null)
                throw new ArgumentNullException("buffer", SR.GetString(SR.ArgumentNull_Array));
            if (offset < 0) 
                throw new ArgumentOutOfRangeException("offset", SR.GetString(SR.ArgumentOutOfRange_NeedPosNum));
            if (count < 0) 
                throw new ArgumentOutOfRangeException("count", SR.GetString(SR.ArgumentOutOfRange_NeedPosNum)); 
            if (count == 0) return; // no need to expend overhead in creating asyncResult, etc.
            if (array.Length - offset < count) 
                throw new ArgumentException("count",SR.GetString(SR.ArgumentOutOfRange_OffsetOut));
            Debug.Assert(timeout == SerialPort.InfiniteTimeout || timeout >= 0, "Serial Stream Write - write timeout is " + timeout);

            // check for open handle, though the port is always supposed to be open 
            if (_handle == null) InternalResources.FileNotOpen();
 
            int numBytes; 
            int hr;
            if (isAsync) { 
                IAsyncResult result = BeginWriteCore(array, offset, count, null, null);
                EndWrite(result);

                SerialStreamAsyncResult afsar = result as SerialStreamAsyncResult; 
                Debug.Assert(afsar != null, "afsar should be a SerialStreamAsyncResult and should not be null");
                numBytes = afsar._numBytes; 
            } 
            else {
                numBytes = WriteFileNative(array, offset, count, null, out hr); 
                if (numBytes == -1) {

                    // This is how writes timeout on Win9x.
                    if (hr == NativeMethods.ERROR_COUNTER_TIMEOUT) 
                        throw new TimeoutException(SR.GetString(SR.Write_timed_out));
 
                    InternalResources.WinIOError(); 
                }
            } 

            if (numBytes == 0)
                throw new TimeoutException(SR.GetString(SR.Write_timed_out));
 
        }
 
        // use default timeout as argument to WriteByte override with timeout arg 
        public override void WriteByte(byte value)
        { 
            WriteByte(value, WriteTimeout);
        }

        internal unsafe void WriteByte(byte value, int timeout) 
        {
            if (inBreak) 
                throw new InvalidOperationException(SR.GetString(SR.In_Break_State)); 

            if (_handle == null) InternalResources.FileNotOpen(); 
            tempBuf[0] = value;


            int numBytes; 
            int hr;
            if (isAsync) { 
                IAsyncResult result = BeginWriteCore(tempBuf, 0, 1, null, null); 
                EndWrite(result);
 
                SerialStreamAsyncResult afsar = result as SerialStreamAsyncResult;
                Debug.Assert(afsar != null, "afsar should be a SerialStreamAsyncResult and should not be null");
                numBytes = afsar._numBytes;
            } 
            else {
                numBytes = WriteFileNative(tempBuf, 0, 1, null, out hr); 
                if (numBytes == -1) { 
                    // This is how writes timeout on Win9x.
                    if (Marshal.GetLastWin32Error() == NativeMethods.ERROR_COUNTER_TIMEOUT) 
                        throw new TimeoutException(SR.GetString(SR.Write_timed_out));

                    InternalResources.WinIOError();
                } 
            }
 
            if (numBytes == 0) 
                throw new TimeoutException(SR.GetString(SR.Write_timed_out));
 
            return;
        }

 

        // --------SUBSECTION: internal-use methods ----------------------* 
        // ------ internal DCB-supporting methods ------- * 

        // Initializes unmananged DCB struct, to be called after opening communications resource. 
        // assumes we have already: baudRate, parity, dataBits, stopBits
        // should only be called in SerialStream(...)
        private void InitializeDCB(int baudRate, Parity parity, int dataBits, StopBits stopBits, bool discardNull)
        { 

            // first get the current dcb structure setup 
            if (UnsafeNativeMethods.GetCommState(_handle, ref dcb) == false) 
            {
                InternalResources.WinIOError(); 
            }
            dcb.DCBlength = (uint) System.Runtime.InteropServices.Marshal.SizeOf(dcb);

            // set parameterized properties 
            dcb.BaudRate = (uint) baudRate;
            dcb.ByteSize = (byte) dataBits; 
 

            switch (stopBits) 
            {
                case StopBits.One:
                    dcb.StopBits = NativeMethods.ONESTOPBIT;
                    break; 
                case StopBits.OnePointFive:
                    dcb.StopBits = NativeMethods.ONE5STOPBITS; 
                    break; 
                case StopBits.Two:
                    dcb.StopBits = NativeMethods.TWOSTOPBITS; 
                    break;
                default:
                    Debug.Assert(false, "Invalid value for stopBits");
                    break; 
            }
 
            dcb.Parity = (byte) parity; 
            // SetDcbFlag, GetDcbFlag expose access to each of the relevant bits of the 32-bit integer
            // storing all flags of the DCB.  C# provides no direct means of manipulating bit fields, so 
            // this is the solution.
            SetDcbFlag(NativeMethods.FPARITY, ((parity == Parity.None)  ?  0  :  1));

            SetDcbFlag(NativeMethods.FBINARY, 1);   // always true for communications resources 

            // set DCB fields implied by default and the arguments given. 
            // Boolean fields in C# must become 1, 0 to properly set the bit flags in the unmanaged DCB struct 

            SetDcbFlag(NativeMethods.FOUTXCTSFLOW, ((handshake == Handshake.RequestToSend || 
                handshake == Handshake.RequestToSendXOnXOff) ? 1 : 0));
            // SetDcbFlag(NativeMethods.FOUTXDSRFLOW, (dsrTimeout != 0L) ? 1 : 0);
            SetDcbFlag(NativeMethods.FOUTXDSRFLOW, 0); // dsrTimeout is always set to 0.
            SetDcbFlag(NativeMethods.FDTRCONTROL, NativeMethods.DTR_CONTROL_DISABLE); 
            SetDcbFlag(NativeMethods.FDSRSENSITIVITY, 0); // this should remain off
            SetDcbFlag(NativeMethods.FINX, (handshake == Handshake.XOnXOff || handshake == Handshake.RequestToSendXOnXOff) ? 1 : 0); 
            SetDcbFlag(NativeMethods.FOUTX,(handshake == Handshake.XOnXOff || handshake == Handshake.RequestToSendXOnXOff) ? 1 : 0); 

            // if no parity, we have no error character (i.e. ErrorChar = '\0' or null character) 
            if (parity != Parity.None)
            {
                SetDcbFlag(NativeMethods.FERRORCHAR, (parityReplace != '\0') ? 1 : 0);
                dcb.ErrorChar = parityReplace; 
            }
            else 
            { 
                SetDcbFlag(NativeMethods.FERRORCHAR, 0);
                dcb.ErrorChar = (byte) '\0'; 
            }

            // this method only runs once in the constructor, so we only have the default value to use.
            // Later the user may change this via the NullDiscard property. 
            SetDcbFlag(NativeMethods.FNULL, discardNull ? 1 : 0);
 
 
            // Setting RTS control, which is RTS_CONTROL_HANDSHAKE if RTS / RTS-XOnXOff handshaking
            // used, RTS_ENABLE (RTS pin used during operation) if rtsEnable true but XOnXoff / No handshaking 
            // used, and disabled otherwise.
            if ((handshake == Handshake.RequestToSend ||
                handshake == Handshake.RequestToSendXOnXOff))
            { 
                SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_HANDSHAKE);
            } 
            else if (GetDcbFlag(NativeMethods.FRTSCONTROL) == NativeMethods.RTS_CONTROL_HANDSHAKE) 
            {
                SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_DISABLE); 
            }

            dcb.XonChar = NativeMethods.DEFAULTXONCHAR;             // may be exposed later but for now, constant
            dcb.XoffChar = NativeMethods.DEFAULTXOFFCHAR; 

            // minimum number of bytes allowed in each buffer before flow control activated 
            // heuristically, this has been set at 1/4 of the buffer size 
            dcb.XonLim = dcb.XoffLim = (ushort) (commProp.dwCurrentRxQueue / 4);
 
            dcb.EofChar = NativeMethods.EOFCHAR;

            //OLD MSCOMM: dcb.EvtChar = (byte) 0;
            // now changed to make use of RXFlag WaitCommEvent event => Eof WaitForCommEvent event 
            dcb.EvtChar = NativeMethods.EOFCHAR;
 
            // set DCB structure 
            if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false)
            { 
                InternalResources.WinIOError();
            }
        }
 
        // Here we provide a method for getting the flags of the Device Control Block structure dcb
        // associated with each instance of SerialStream, i.e. this method gets myStream.dcb.Flags 
        // Flags are any of the constants in NativeMethods such as FBINARY, FDTRCONTROL, etc. 
        internal int GetDcbFlag(int whichFlag)
        { 
            uint mask;

            Debug.Assert(whichFlag >= NativeMethods.FBINARY && whichFlag <= NativeMethods.FDUMMY2, "GetDcbFlag needs to fit into enum!");
 
            if (whichFlag == NativeMethods.FDTRCONTROL || whichFlag == NativeMethods.FRTSCONTROL)
            { 
                mask = 0x3; 
            }
            else if (whichFlag == NativeMethods.FDUMMY2) 
            {
                mask = 0x1FFFF;
            }
            else 
            {
                mask = 0x1; 
            } 
            uint result = dcb.Flags & (mask << whichFlag);
            return (int) (result >> whichFlag); 
        }

        // Since C# applications have to provide a workaround for accessing and setting bitfields in unmanaged code,
        // here we provide methods for getting and setting the Flags field of the Device Control Block structure dcb 
        // associated with each instance of SerialStream, i.e. this method sets myStream.dcb.Flags
        // Flags are any of the constants in NativeMethods such as FBINARY, FDTRCONTROL, etc. 
        internal void SetDcbFlag(int whichFlag, int setting) 
        {
            uint mask; 
            setting = setting << whichFlag;

            Debug.Assert(whichFlag >= NativeMethods.FBINARY && whichFlag <= NativeMethods.FDUMMY2, "SetDcbFlag needs to fit into enum!");
 
            if (whichFlag == NativeMethods.FDTRCONTROL || whichFlag == NativeMethods.FRTSCONTROL)
            { 
                mask = 0x3; 
            }
            else if (whichFlag == NativeMethods.FDUMMY2) 
            {
                mask = 0x1FFFF;
            }
            else 
            {
                mask = 0x1; 
            } 

            // clear the region 
            dcb.Flags &= ~(mask << whichFlag);

            // set the region
            dcb.Flags |= ((uint) setting); 
        }
 
        // ----SUBSECTION: internal methods supporting public read/write methods-------* 

        unsafe private SerialStreamAsyncResult BeginReadCore(byte[] array, int offset, int numBytes, AsyncCallback userCallback, Object stateObject) 
        {

            // Create and store async stream class library specific data in the
            // async result 
            SerialStreamAsyncResult asyncResult = new SerialStreamAsyncResult();
            asyncResult._userCallback = userCallback; 
            asyncResult._userStateObject = stateObject; 
            asyncResult._isWrite = false;
 
            // For Synchronous IO, I could go with either a callback and using
            // the managed Monitor class, or I could create a handle and wait on it.
            ManualResetEvent waitHandle = new ManualResetEvent(false);
            asyncResult._waitHandle = waitHandle; 

            // Create a managed overlapped class 
            // We will set the file offsets later 
            Overlapped overlapped = new Overlapped(0, 0, IntPtr.Zero, asyncResult);
 
            // Pack the Overlapped class, and store it in the async result
            NativeOverlapped* intOverlapped = overlapped.Pack(IOCallback, null);
            asyncResult._overlapped = intOverlapped;
 
            // Keep the array in one location in memory until the OS writes the
            // relevant data into the array.  Free GCHandle later. 
            asyncResult.PinBuffer(array); 

            // queue an async ReadFile operation and pass in a packed overlapped 
            //int r = ReadFile(_handle, array, numBytes, null, intOverlapped);
            int hr = 0;
            int r = ReadFileNative(array, offset, numBytes,
             intOverlapped, out hr); 

            // ReadFile, the OS version, will return 0 on failure.  But 
            // my ReadFileNative wrapper returns -1.  My wrapper will return 
            // the following:
            // On error, r==-1. 
            // On async requests that are still pending, r==-1 w/ hr==ERROR_IO_PENDING
            // on async requests that completed sequentially, r==0
            // Note that you will NEVER RELIABLY be able to get the number of bytes
            // read back from this call when using overlapped structures!  You must 
            // not pass in a non-null lpNumBytesRead to ReadFile when using
            // overlapped structures! 
            if (r==-1) 
            {
                if (hr != NativeMethods.ERROR_IO_PENDING) 
                {
                    if (hr == NativeMethods.ERROR_HANDLE_EOF)
                        InternalResources.EndOfFile();
                    else 
                        InternalResources.WinIOError(hr, String.Empty);
                } 
            } 

            return asyncResult; 
        }

        unsafe private SerialStreamAsyncResult BeginWriteCore(byte[] array, int offset, int numBytes, AsyncCallback userCallback, Object stateObject)
        { 
            // Create and store async stream class library specific data in the
            // async result 
            SerialStreamAsyncResult asyncResult = new SerialStreamAsyncResult(); 
            asyncResult._userCallback = userCallback;
            asyncResult._userStateObject = stateObject; 
            asyncResult._isWrite = true;

            // For Synchronous IO, I could go with either a callback and using
            // the managed Monitor class, or I could create a handle and wait on it. 
            ManualResetEvent waitHandle = new ManualResetEvent(false);
            asyncResult._waitHandle = waitHandle; 
 
            // Create a managed overlapped class
            // We will set the file offsets later 
            Overlapped overlapped = new Overlapped(0, 0, IntPtr.Zero, asyncResult);

            // Pack the Overlapped class, and store it in the async result
            NativeOverlapped* intOverlapped = overlapped.Pack(IOCallback, null); 
            asyncResult._overlapped = intOverlapped;
 
 
            // Keep the array in one location in memory until the OS reads the
            // relevant data from the array.  Free GCHandle later. 
            asyncResult.PinBuffer(array);

            int hr = 0;
            // queue an async WriteFile operation and pass in a packed overlapped 
            int r = WriteFileNative(array, offset, numBytes, intOverlapped, out hr);
 
            // WriteFile, the OS version, will return 0 on failure.  But 
            // my WriteFileNative wrapper returns -1.  My wrapper will return
            // the following: 
            // On error, r==-1.
            // On async requests that are still pending, r==-1 w/ hr==ERROR_IO_PENDING
            // On async requests that completed sequentially, r==0
            // Note that you will NEVER RELIABLY be able to get the number of bytes 
            // written back from this call when using overlapped IO!  You must
            // not pass in a non-null lpNumBytesWritten to WriteFile when using 
            // overlapped structures! 
            if (r==-1)
            { 
                if (hr != NativeMethods.ERROR_IO_PENDING)
                {

                    if (hr == NativeMethods.ERROR_HANDLE_EOF) 
                        InternalResources.EndOfFile();
                    else 
                        InternalResources.WinIOError(hr, String.Empty); 
                }
            } 
            return asyncResult;
        }

 
        // Internal method, wrapping the PInvoke to ReadFile().
        private unsafe int ReadFileNative(byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int hr) 
        { 

            // Don't corrupt memory when multiple threads are erroneously writing 
            // to this stream simultaneously.
            if (bytes.Length - offset < count)
                throw new IndexOutOfRangeException(SR.GetString(SR.IndexOutOfRange_IORaceCondition));
 
            // You can't use the fixed statement on an array of length 0.
            if (bytes.Length==0) 
            { 
                hr = 0;
                return 0; 
            }

            int r = 0;
            int numBytesRead = 0; 

            fixed(byte* p = bytes) 
            { 
                if (isAsync)
                    r = UnsafeNativeMethods.ReadFile(_handle, p + offset, count, IntPtr.Zero, overlapped); 
                else
                    r = UnsafeNativeMethods.ReadFile(_handle, p + offset, count, out numBytesRead, IntPtr.Zero);
            }
 
            if (r==0)
            { 
                hr = Marshal.GetLastWin32Error(); 

                // Note: we should never silently ---- an error here without some 
                // extra work.  We must make sure that BeginReadCore won't return an
                // IAsyncResult that will cause EndRead to block, since the OS won't
                // call AsyncFSCallback for us.
 
                // For invalid handles, detect the error and mark our handle
                // as closed to give slightly better error messages.  Also 
                // help ensure we avoid handle recycling bugs. 
                if (hr == NativeMethods.ERROR_INVALID_HANDLE)
                    _handle.SetHandleAsInvalid(); 

                return -1;
            }
            else 
                hr = 0;
            return numBytesRead; 
        } 

        private unsafe int WriteFileNative(byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int hr) 
        {

            // Don't corrupt memory when multiple threads are erroneously writing
            // to this stream simultaneously.  (Note that the OS is reading from 
            // the array we pass to WriteFile, but if we read beyond the end and
            // that memory isn't allocated, we could get an AV.) 
            if (bytes.Length - offset < count) 
                throw new IndexOutOfRangeException(SR.GetString(SR.IndexOutOfRange_IORaceCondition));
 
            // You can't use the fixed statement on an array of length 0.
            if (bytes.Length==0)
            {
                hr = 0; 
                return 0;
            } 
 
            int numBytesWritten = 0;
            int r = 0; 

            fixed(byte* p = bytes)
            {
                if (isAsync) 
                    r = UnsafeNativeMethods.WriteFile(_handle, p + offset, count, IntPtr.Zero, overlapped);
                else 
                    r = UnsafeNativeMethods.WriteFile(_handle, p + offset, count, out numBytesWritten, IntPtr.Zero); 
            }
 
            if (r==0)
            {
                hr = Marshal.GetLastWin32Error();
                // Note: we should never silently ---- an error here without some 
                // extra work.  We must make sure that BeginWriteCore won't return an
                // IAsyncResult that will cause EndWrite to block, since the OS won't 
                // call AsyncFSCallback for us. 

                // For invalid handles, detect the error and mark our handle 
                // as closed to give slightly better error messages.  Also
                // help ensure we avoid handle recycling bugs.
                if (hr == NativeMethods.ERROR_INVALID_HANDLE)
                    _handle.SetHandleAsInvalid(); 

                return -1; 
            } 
            else
                hr = 0; 
            return numBytesWritten;
        }

        // ----SUBSECTION: internal methods supporting events/async operation------* 

        // This is a the callback prompted when a thread completes any async I/O operation. 
        unsafe private static void AsyncFSCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped) 
        {
            // Unpack overlapped 
            Overlapped overlapped = Overlapped.Unpack(pOverlapped);

            // Extract async the result from overlapped structure
            SerialStreamAsyncResult asyncResult = 
                (SerialStreamAsyncResult)overlapped.AsyncResult;
            asyncResult._numBytes = (int)numBytes; 
 
            asyncResult._errorCode = (int)errorCode;
 
            // Call the user-provided callback.  Note that it can and often should
            // call EndRead or EndWrite.  There's no reason to use an async
            // delegate here - we're already on a threadpool thread.
            // Note the IAsyncResult's completedSynchronously property must return 
            // false here, saying the user callback was called on another thread.
            asyncResult._completedSynchronously = false; 
            asyncResult._isComplete = true; 

            // The OS does not signal this event.  We must do it ourselves. 
            // But don't close it if the user callback called EndXxx,
            // which then closed the manual reset event already.
            ManualResetEvent wh = asyncResult._waitHandle;
            if (wh != null) { 
                bool r = wh.Set();
                if (!r) InternalResources.WinIOError(); 
            } 

            AsyncCallback userCallback = asyncResult._userCallback; 
            if (userCallback != null)
                userCallback(asyncResult);
        }
 

        // ----SECTION: internal classes --------* 
 
        internal sealed class EventLoopRunner {
            private WeakReference streamWeakReference; 
            internal ManualResetEvent eventLoopEndedSignal = new ManualResetEvent(false);
            internal ManualResetEvent waitCommEventWaitHandle = new ManualResetEvent(false);
            private SafeFileHandle handle = null;
            private bool isAsync; 
            internal bool endEventLoop;
            private int eventsOccurred; 
 
            WaitCallback callErrorEvents;
            WaitCallback callReceiveEvents; 
            WaitCallback callPinEvents;
            IOCompletionCallback freeNativeOverlappedCallback;

#if DEBUG 
            string portName;
#endif 
            internal unsafe EventLoopRunner(SerialStream stream) { 
                handle = stream._handle;
                streamWeakReference = new WeakReference(stream); 

                callErrorEvents = new WaitCallback(CallErrorEvents);
                callReceiveEvents = new WaitCallback(CallReceiveEvents );
                callPinEvents = new WaitCallback(CallPinEvents); 
                freeNativeOverlappedCallback = new IOCompletionCallback(FreeNativeOverlappedCallback);
                isAsync = stream.isAsync; 
#if DEBUG 
                portName = stream.portName;
#endif 
            }

            internal bool ShutdownLoop {
                get { 
                    return endEventLoop;
                } 
            } 

            // This is the blocking method that waits for an event to occur.  It wraps the SDK's WaitCommEvent function. 
            internal unsafe void WaitForCommEvent()
            {
                int unused = 0;
                bool doCleanup = false; 
                NativeOverlapped* intOverlapped = null;
                while (!ShutdownLoop) { 
                    SerialStreamAsyncResult asyncResult = null; 
                    if (isAsync) {
                        asyncResult = new SerialStreamAsyncResult(); 
                        asyncResult._userCallback = null;
                        asyncResult._userStateObject = null;
                        asyncResult._isWrite = false;
 
                        // we're going to use _numBytes for something different in this loop.  In this case, both
                        // freeNativeOverlappedCallback and this thread will decrement that value.  Whichever one decrements it 
                        // to zero will be the one to free the native overlapped.  This guarantees the overlapped gets freed 
                        // after both the callback and GetOverlappedResult have had a chance to use it.
                        asyncResult._numBytes = 2; 
                        asyncResult._waitHandle = waitCommEventWaitHandle;

                        waitCommEventWaitHandle.Reset();
                        Overlapped overlapped = new Overlapped(0, 0, waitCommEventWaitHandle.SafeWaitHandle.DangerousGetHandle(), asyncResult); 
                        // Pack the Overlapped class, and store it in the async result
                        intOverlapped = overlapped.Pack(freeNativeOverlappedCallback, null); 
                    } 

                    fixed (int* eventsOccurredPtr = &eventsOccurred) { 

                    if (UnsafeNativeMethods.WaitCommEvent(handle, eventsOccurredPtr, intOverlapped) == false)
                    {
                        int hr = Marshal.GetLastWin32Error(); 
                        if (hr == NativeMethods.ERROR_ACCESS_DENIED) {
                            doCleanup = true; 
                            break; 
                        }
                        if (hr == NativeMethods.ERROR_IO_PENDING) 
                        {
                            Debug.Assert(isAsync, "The port is not open for async, so we should not get ERROR_IO_PENDING from WaitCommEvent");
                            int error;
 
                            // if we get IO pending, MSDN says we should wait on the WaitHandle, then call GetOverlappedResult
                            // to get the results of WaitCommEvent. 
                            bool success = waitCommEventWaitHandle.WaitOne(); 
                            Debug.Assert(success, "waitCommEventWaitHandle.WaitOne() returned error " + Marshal.GetLastWin32Error());
 
                            do {
                                // NOTE: GetOverlappedResult will modify the original pointer passed into WaitCommEvent.
                                success = UnsafeNativeMethods.GetOverlappedResult(handle, intOverlapped, ref unused, false);
                                error = Marshal.GetLastWin32Error(); 
                            }
                            while (error == NativeMethods.ERROR_IO_INCOMPLETE && !ShutdownLoop && !success); 
 
                            if (!success) {
                                // Ignore ERROR_IO_INCOMPLETE and ERROR_INVALID_PARAMETER, because there's a chance we'll get 
                                // one of those while shutting down
                                if (! ( (error == NativeMethods.ERROR_IO_INCOMPLETE || error == NativeMethods.ERROR_INVALID_PARAMETER) && ShutdownLoop))
                                    Debug.Assert(false, "GetOverlappedResult returned error, we might leak intOverlapped memory" + error.ToString(CultureInfo.InvariantCulture));
                            } 
                        }
                        else if (hr != NativeMethods.ERROR_INVALID_PARAMETER) { 
                            // ignore ERROR_INVALID_PARAMETER errors.  WaitCommError seems to return this 
                            // when SetCommMask is changed while it's blocking (like we do in Dispose())
                            Debug.Assert(false, "WaitCommEvent returned error " + hr); 
                        }
                    }
                    }
 
                    if (!ShutdownLoop)
                        CallEvents(eventsOccurred); 
 
                    if (isAsync) {
                        if (Interlocked.Decrement(ref asyncResult._numBytes) == 0) 
                            Overlapped.Free(intOverlapped);
                    }
                }
                if (doCleanup) { 
                    // the rest will be handled in Dispose()
                    endEventLoop = true; 
                    Overlapped.Free(intOverlapped); 
                }
                eventLoopEndedSignal.Set(); 
            }

            private unsafe void FreeNativeOverlappedCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped) {
                // Unpack overlapped 
                Overlapped overlapped = Overlapped.Unpack(pOverlapped);
 
                // Extract the async result from overlapped structure 
                SerialStreamAsyncResult asyncResult =
                    (SerialStreamAsyncResult)overlapped.AsyncResult; 

                if (Interlocked.Decrement(ref asyncResult._numBytes) == 0)
                    Overlapped.Free(pOverlapped);
            } 

            private void CallEvents(int nativeEvents) 
            { 
                // first see if we had any error conditions
                if ((nativeEvents & NativeMethods.EV_ERR) != 0) { 
                    int errors = 0;
                    if (UnsafeNativeMethods.ClearCommError(handle, ref errors, IntPtr.Zero) == false) {
                        InternalResources.WinIOError();
                    } 

                    errors = errors & errorEvents; 
                    // 

 

                    if (errors != 0) {
                        ThreadPool.QueueUserWorkItem(callErrorEvents, errors);
                    } 
                }
 
                // now look for pin changed and received events. 
                if ((nativeEvents & pinChangedEvents) != 0) {
                    ThreadPool.QueueUserWorkItem(callPinEvents, nativeEvents); 
                }

                if ((nativeEvents & receivedEvents) != 0) {
                    ThreadPool.QueueUserWorkItem(callReceiveEvents, nativeEvents); 
                }
            } 
 

            private void CallErrorEvents(object state) { 
                int errors = (int) state;
                SerialStream stream = (SerialStream) streamWeakReference.Target;
                if (stream == null)
                    return; 

                if (stream.ErrorReceived != null) { 
                    if ((errors & (int) SerialError.TXFull) != 0) 
                        stream.ErrorReceived(stream, new SerialErrorReceivedEventArgs(SerialError.TXFull));
 
                    if ((errors & (int) SerialError.RXOver) != 0)
                        stream.ErrorReceived(stream, new SerialErrorReceivedEventArgs(SerialError.RXOver));

                    if ((errors & (int) SerialError.Overrun) != 0) 
                        stream.ErrorReceived(stream, new SerialErrorReceivedEventArgs(SerialError.Overrun));
 
                    if ((errors & (int) SerialError.RXParity) != 0) 
                        stream.ErrorReceived(stream, new SerialErrorReceivedEventArgs(SerialError.RXParity));
 
                    if ((errors & (int) SerialError.Frame) != 0)
                        stream.ErrorReceived(stream, new SerialErrorReceivedEventArgs(SerialError.Frame));
                }
 
                stream = null;
            } 
 
            private void CallReceiveEvents(object state) {
                int nativeEvents = (int) state; 
                SerialStream stream = (SerialStream) streamWeakReference.Target;
                if (stream == null)
                    return;
 
                if (stream.DataReceived != null) {
                    if ((nativeEvents & (int) SerialData.Chars) != 0) 
                        stream.DataReceived(stream, new SerialDataReceivedEventArgs(SerialData.Chars)); 
                    if ((nativeEvents & (int) SerialData.Eof) != 0)
                        stream.DataReceived(stream, new SerialDataReceivedEventArgs(SerialData.Eof)); 
                }

                stream = null;
            } 

            private void CallPinEvents(object state) { 
                int nativeEvents = (int) state; 

                SerialStream stream = (SerialStream) streamWeakReference.Target; 
                if (stream == null)
                    return;

                if (stream.PinChanged != null) { 
                    if ((nativeEvents & (int) SerialPinChange.CtsChanged) != 0)
                        stream.PinChanged(stream, new SerialPinChangedEventArgs(SerialPinChange.CtsChanged)); 
 
                    if ((nativeEvents & (int) SerialPinChange.DsrChanged) != 0)
                        stream.PinChanged(stream, new SerialPinChangedEventArgs(SerialPinChange.DsrChanged)); 

                    if ((nativeEvents & (int) SerialPinChange.CDChanged) != 0)
                        stream.PinChanged(stream, new SerialPinChangedEventArgs(SerialPinChange.CDChanged));
 
                    if ((nativeEvents & (int) SerialPinChange.Ring) != 0)
                        stream.PinChanged(stream, new SerialPinChangedEventArgs(SerialPinChange.Ring)); 
 
                    if ((nativeEvents & (int) SerialPinChange.Break) != 0)
                        stream.PinChanged(stream, new SerialPinChangedEventArgs(SerialPinChange.Break)); 
                }

                stream = null;
            } 

        } 
 

        // This is an internal object implementing IAsyncResult with fields 
        // for all of the relevant data necessary to complete the IO operation.
        // This is used by AsyncFSCallback and all async methods.
        unsafe internal sealed class SerialStreamAsyncResult : IAsyncResult
        { 
            // User code callback
            internal AsyncCallback _userCallback; 
 
            internal Object _userStateObject;
            internal GCHandle _bufferHandle;  // GCHandle to pin byte[]. 

            internal bool _isWrite;     // Whether this is a read or a write
            internal bool _isComplete;
            internal bool _completedSynchronously;  // Which thread called callback 
            internal bool _bufferIsPinned;   // Whether our _bufferHandle is valid.
 
            internal ManualResetEvent _waitHandle; 
            internal int _EndXxxCalled;   // Whether we've called EndXxx already.
            internal int _numBytes;     // number of bytes read OR written 
            internal int _errorCode;
            internal NativeOverlapped* _overlapped;

            public Object AsyncState 
            {
                get { return _userStateObject; } 
            } 

            public bool IsCompleted 
            {
                get { return _isComplete; }
            }
 
            public WaitHandle AsyncWaitHandle
            { 
                get { 
                    /*
                      // Consider uncommenting this someday soon - the EventHandle 
                      // in the Overlapped struct is really useless half of the
                      // time today since the OS doesn't signal it.  If users call
                      // EndXxx after the OS call happened to complete, there's no
                      // reason to create a synchronization primitive here.  Fixing 
                      // this will save us some perf, assuming we can correctly
                      // initialize the ManualResetEvent. 
                    if (_waitHandle == null) { 
                        ManualResetEvent mre = new ManualResetEvent(false);
                        if (_overlapped != null && _overlapped->EventHandle != IntPtr.Zero) 
                            mre.Handle = _overlapped->EventHandle;
                        if (_isComplete)
                            mre.Set();
                        _waitHandle = mre; 
                    }
                    */ 
                    return _waitHandle; 
                }
            } 

            // Returns true iff the user callback was called by the thread that
            // called BeginRead or BeginWrite.  If we use an async delegate or
            // threadpool thread internally, this will be false.  This is used 
            // by code to determine whether a successive call to BeginRead needs
            // to be done on their main thread or in their callback to avoid a 
            // stack overflow on many reads or writes. 
            public bool CompletedSynchronously
            { 
                get { return _completedSynchronously; }
            }

            // Methods to ensure that if we somehow return from Win32 ReadFile() or WriteFile() 
            // and fill the buffer at different times, the GC doesn't move said buffer.
            internal void PinBuffer(byte[] buffer) 
            { 
                _bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                _bufferIsPinned = true; 
            }

            internal void UnpinBuffer()
            { 
                if (_bufferIsPinned)
                { 
                    _bufferHandle.Free(); 
                    _bufferIsPinned = false;
                } 
            }
        }
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 
/*==============================================================================
** 
** Class: SerialStream 
**
** Purpose: Class for enabling low-level [....] and async control over a serial 
**        : communications resource.
**
** Date: August, 2002
** 
=============================================================================*/
 
using System; 
using System.IO;
using System.Text; 
using System.ComponentModel;
using System.Resources;
using System.Runtime;
using System.Security; 
using System.Security.Permissions;
using System.Runtime.InteropServices; 
using System.Diagnostics; 
using System.Collections;
using Microsoft.Win32; 
using Microsoft.Win32.SafeHandles;
using System.Threading;
using System.Runtime.Remoting.Messaging;
using System.Runtime.CompilerServices; 
using System.Globalization;
using System.Runtime.Versioning; 
 
// Notes about the SerialStream:
//  * The stream is always opened via the SerialStream constructor. 
//  * The handleProtector guarantees ownership of the file handle, so that it may not be
//  * unnaturally closed by another process or thread.  Thus, since all properties are available
//  * only when the object exists, the object's properties can be queried only when the SerialStream
//  * object is instantiated (i.e. "open"). 
//  * Handles to serial communications resources here always:
//  * 1) own the handle 
//  * 2) are opened for asynchronous operation 
//  * 3) set access at the level of FileAccess.ReadWrite
//  * 4) Allow for reading AND writing 
//  * 5) Disallow seeking, since they encapsulate a file of type FILE_TYPE_CHAR

namespace System.IO.Ports
{ 
    internal sealed class SerialStream : Stream
    { 
        const int errorEvents = (int) (SerialError.Frame | SerialError.Overrun | 
                                 SerialError.RXOver | SerialError.RXParity | SerialError.TXFull);
        const int receivedEvents = (int) (SerialData.Chars | SerialData.Eof); 
        const int pinChangedEvents = (int) (SerialPinChange.Break | SerialPinChange.CDChanged | SerialPinChange.CtsChanged |
                                      SerialPinChange.Ring | SerialPinChange.DsrChanged);

        const int infiniteTimeoutConst = -2; 

        // members supporting properties exposed to SerialPort 
        private string portName; 
        private byte parityReplace = (byte) '?';
        private bool inBreak = false;               // port is initially in non-break state 
        private bool isAsync = true;
        private Handshake handshake;
        private bool rtsEnable = false;
 
        // The internal C# representations of Win32 structures necessary for communication
        // hold most of the internal "fields" maintaining information about the port. 
        private UnsafeNativeMethods.DCB dcb; 
        private UnsafeNativeMethods.COMMTIMEOUTS commTimeouts;
        private UnsafeNativeMethods.COMSTAT comStat; 
        private UnsafeNativeMethods.COMMPROP commProp;

        // internal-use members
        // private const long dsrTimeout = 0L; -- Not used anymore. 
        private const int maxDataBits = 8;
        private const int minDataBits = 5; 
        internal SafeFileHandle _handle = null; 
        internal EventLoopRunner eventRunner;
 
        private byte[] tempBuf;                 // used to avoid multiple array allocations in ReadByte()

        // called whenever any async i/o operation completes.
        private unsafe static readonly IOCompletionCallback IOCallback = new IOCompletionCallback(SerialStream.AsyncFSCallback); 

        // three different events, also wrapped by SerialPort. 
        internal event SerialDataReceivedEventHandler   DataReceived;      // called when one character is received. 
        internal event SerialPinChangedEventHandler PinChanged;    // called when any of the pin/ring-related triggers occurs
        internal event SerialErrorReceivedEventHandler      ErrorReceived;         // called when any runtime error occurs on the port (frame, overrun, parity, etc.) 


        // ----SECTION: inherited properties from Stream class ------------*
 
        // These six properites are required for SerialStream to inherit from the abstract Stream class.
        // Note four of them are always true or false, and two of them throw exceptions, so these 
        // are not usefully queried by applications which know they have a SerialStream, etc... 
        public override bool CanRead
        { 
            get { return (_handle != null); }
        }

        public override bool CanSeek 
        {
            get { return false; } 
        } 

        public override bool CanTimeout { 
            get { return (_handle != null); }
        }

        public override bool CanWrite 
        {
            get { return (_handle != null); } 
        } 

        public override long Length 
        {
            get { throw new NotSupportedException(SR.GetString(SR.NotSupported_UnseekableStream)); }
        }
 

        public override long Position 
        { 
            get { throw new NotSupportedException(SR.GetString(SR.NotSupported_UnseekableStream)); }
            set { throw new NotSupportedException(SR.GetString(SR.NotSupported_UnseekableStream)); } 
        }

        // ----- new get-set properties -----------------*
 
        // Standard port properties, also called from SerialPort
        // BaudRate may not be settable to an arbitrary integer between dwMinBaud and dwMaxBaud, 
        // and is limited only by the serial driver.  Typically about twelve values such 
        // as Winbase.h's CBR_110 through CBR_256000 are used.
        internal int BaudRate 
        {
            //get { return (int) dcb.BaudRate; }
            set
            { 
                if (value <= 0 || (value > commProp.dwMaxBaud && commProp.dwMaxBaud > 0))
                { 
                    // if no upper bound on baud rate imposed by serial driver, note that argument must be positive 
                    if (commProp.dwMaxBaud == 0)
                    { 
                        throw new ArgumentOutOfRangeException("baudRate",
                            SR.GetString(SR.ArgumentOutOfRange_NeedPosNum));
                    }
                    else 
                    {
                        // otherwise, we can present the bounds on the baud rate for this driver 
                        throw new ArgumentOutOfRangeException("baudRate", 
                            SR.GetString(SR.ArgumentOutOfRange_Bounds_Lower_Upper, 0, commProp.dwMaxBaud));
                    } 
                }
                // Set only if it's different.  Rollback to previous values if setting fails.
                //  This pattern occurs through most of the other properties in this class.
                if(value != dcb.BaudRate) 
                {
                    int baudRateOld = (int) dcb.BaudRate; 
                    dcb.BaudRate = (uint) value; 

                    if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false) 
                    {
                        dcb.BaudRate = (uint) baudRateOld;
                        InternalResources.WinIOError();
                    } 
                }
            } 
        } 

        public bool BreakState { 
            get { return inBreak; }
            set {
                if (value) {
                    if (UnsafeNativeMethods.SetCommBreak(_handle) == false) 
                        InternalResources.WinIOError();
                    inBreak = true; 
                } 
                else {
                    if (UnsafeNativeMethods.ClearCommBreak(_handle) == false) 
                        InternalResources.WinIOError();
                    inBreak = false;
                }
            } 
        }
 
        internal int DataBits 
        {
            //get  { return (int) dcb.ByteSize; } 
            set
            {
                Debug.Assert(!(value < minDataBits || value > maxDataBits), "An invalid value was passed to DataBits");
                if (value != dcb.ByteSize) 
                {
                    byte byteSizeOld = dcb.ByteSize; 
                    dcb.ByteSize = (byte) value; 

                    if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false) 
                    {
                        dcb.ByteSize = byteSizeOld;
                        InternalResources.WinIOError();
                    } 
                }
            } 
        } 

 
        internal bool DiscardNull
        {
            //get {   return (GetDcbFlag(NativeMethods.FNULL) == 1);}
            set 
            {
                int fNullFlag = GetDcbFlag(NativeMethods.FNULL); 
                if(value == true && fNullFlag == 0 || value == false && fNullFlag == 1) 
                {
                    int fNullOld = fNullFlag; 
                    SetDcbFlag(NativeMethods.FNULL, value ? 1 : 0);

                    if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false)
                    { 
                        SetDcbFlag(NativeMethods.FNULL, fNullOld);
                        InternalResources.WinIOError(); 
                    } 
                }
            } 
        }

        internal bool DtrEnable
        { 
            get {
                int fDtrControl = GetDcbFlag(NativeMethods.FDTRCONTROL); 
 
                return (fDtrControl == NativeMethods.DTR_CONTROL_ENABLE);
            } 
            set
            {
                // first set the FDTRCONTROL field in the DCB struct
                int fDtrControlOld = GetDcbFlag(NativeMethods.FDTRCONTROL); 

                SetDcbFlag(NativeMethods.FDTRCONTROL, value ? NativeMethods.DTR_CONTROL_ENABLE : NativeMethods.DTR_CONTROL_DISABLE); 
                if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false) 
                {
                    SetDcbFlag(NativeMethods.FDTRCONTROL, fDtrControlOld); 
                    InternalResources.WinIOError();
                }

                // then set the actual pin 
                if (!UnsafeNativeMethods.EscapeCommFunction(_handle, value ? NativeMethods.SETDTR : NativeMethods.CLRDTR))
                    InternalResources.WinIOError(); 
 
            }
        } 

        internal Handshake Handshake
        {
            //get  { return handshake; } 
            set
            { 
 
                Debug.Assert(!(value < System.IO.Ports.Handshake.None || value > System.IO.Ports.Handshake.RequestToSendXOnXOff),
                    "An invalid value was passed to Handshake"); 

                if(value != handshake)
                {
                    // in the DCB, handshake affects the fRtsControl, fOutxCtsFlow, and fInX, fOutX fields, 
                    // so we must save everything in that closure before making any changes.
                    Handshake handshakeOld = handshake; 
                    int fInOutXOld = GetDcbFlag(NativeMethods.FINX); 
                    int fOutxCtsFlowOld = GetDcbFlag(NativeMethods.FOUTXCTSFLOW);
                    int fRtsControlOld = GetDcbFlag(NativeMethods.FRTSCONTROL); 

                    handshake = value;
                    int fInXOutXFlag = (handshake == Handshake.XOnXOff || handshake == Handshake.RequestToSendXOnXOff) ? 1 : 0;
                    SetDcbFlag(NativeMethods.FINX, fInXOutXFlag); 
                    SetDcbFlag(NativeMethods.FOUTX, fInXOutXFlag);
 
                    SetDcbFlag(NativeMethods.FOUTXCTSFLOW, (handshake == Handshake.RequestToSend || 
                        handshake == Handshake.RequestToSendXOnXOff) ? 1 : 0);
 
                    if ((handshake == Handshake.RequestToSend ||
                        handshake == Handshake.RequestToSendXOnXOff))
                    {
                        SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_HANDSHAKE); 
                    }
                    else if (rtsEnable) 
                    { 
                        SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_ENABLE);
                    } 
                    else {
                        SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_DISABLE);
                    }
 
                    if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false)
                    { 
                        handshake = handshakeOld; 
                        SetDcbFlag(NativeMethods.FINX, fInOutXOld);
                        SetDcbFlag(NativeMethods.FOUTX, fInOutXOld); 
                        SetDcbFlag(NativeMethods.FOUTXCTSFLOW, fOutxCtsFlowOld);
                        SetDcbFlag(NativeMethods.FRTSCONTROL, fRtsControlOld);
                        InternalResources.WinIOError();
                    } 

                } 
            } 
        }
 
        internal bool IsOpen {
            get {
                return _handle != null && !eventRunner.ShutdownLoop;
            } 
        }
 
        internal Parity Parity 
        {
            //get     {   return (Parity) dcb.Parity;     } 
            set
            {
                Debug.Assert(!(value < Parity.None || value > Parity.Space), "An invalid value was passed to Parity");
 
                if((byte) value != dcb.Parity)
                { 
                    byte parityOld = dcb.Parity; 

                    // in the DCB structure, the parity setting also potentially effects: 
                    // fParity, fErrorChar, ErrorChar
                    // so these must be saved as well.
                    int fParityOld = GetDcbFlag(NativeMethods.FPARITY);
                    byte ErrorCharOld = dcb.ErrorChar; 
                    int fErrorCharOld = GetDcbFlag(NativeMethods.FERRORCHAR);
                    dcb.Parity = (byte) value; 
 
                    int parityFlag = (dcb.Parity == (byte) Parity.None) ? 0 : 1;
                    SetDcbFlag(NativeMethods.FPARITY, parityFlag); 
                    if (parityFlag == 1)
                    {
                        SetDcbFlag(NativeMethods.FERRORCHAR, (parityReplace != '\0') ? 1 : 0);
                        dcb.ErrorChar = parityReplace; 
                    }
                    else 
                    { 
                        SetDcbFlag(NativeMethods.FERRORCHAR, 0);
                        dcb.ErrorChar = (byte) '\0'; 
                    }
                    if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false)
                    {
                        dcb.Parity = parityOld; 
                        SetDcbFlag(NativeMethods.FPARITY, fParityOld);
 
                        dcb.ErrorChar = ErrorCharOld; 
                        SetDcbFlag(NativeMethods.FERRORCHAR, fErrorCharOld);
 
                        InternalResources.WinIOError();
                    }
                }
            } 
        }
 
        // ParityReplace is the eight-bit character which replaces any bytes which 
        // ParityReplace affects the equivalent field in the DCB structure: ErrorChar, and
        // the DCB flag fErrorChar. 
        internal byte ParityReplace
        {
            //get {   return parityReplace; }
            set 
            {
                if(value != parityReplace) 
                { 
                    byte parityReplaceOld = parityReplace;
                    byte errorCharOld = dcb.ErrorChar; 
                    int fErrorCharOld = GetDcbFlag(NativeMethods.FERRORCHAR);

                    parityReplace = value;
                    if (GetDcbFlag(NativeMethods.FPARITY) == 1) 
                    {
                        SetDcbFlag(NativeMethods.FERRORCHAR, (parityReplace != '\0')? 1 : 0); 
                        dcb.ErrorChar = parityReplace; 
                    }
                    else 
                    {
                        SetDcbFlag(NativeMethods.FERRORCHAR, 0);
                        dcb.ErrorChar = (byte) '\0';
                    } 

 
                    if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false) 
                    {
                        parityReplace = parityReplaceOld; 
                        SetDcbFlag(NativeMethods.FERRORCHAR, fErrorCharOld);
                        dcb.ErrorChar = errorCharOld;
                        InternalResources.WinIOError();
                    } 
                }
            } 
        } 

        // Timeouts are considered to be TOTAL time for the Read/Write operation and to be in milliseconds. 
        // Timeouts are translated into DCB structure as follows:
        // Desired timeout      =>  ReadTotalTimeoutConstant    ReadTotalTimeoutMultiplier  ReadIntervalTimeout
        //  0                                   0                           0               MAXDWORD
        //  0 < n < infinity                    n                       MAXDWORD            MAXDWORD 
        // infinity                             infiniteTimeoutConst    MAXDWORD            MAXDWORD
        // 
        // rationale for "infinity": There does not exist in the COMMTIMEOUTS structure a way to 
        // *wait indefinitely for any byte, return when found*.  Instead, if we set ReadTimeout
        // to infinity, SerialStream's EndRead loops if infiniteTimeoutConst mills have elapsed 
        // without a byte received.  Note that this is approximately 24 days, so essentially
        // most practical purposes effectively equate 24 days with an infinite amount of time
        // on a serial port connection.
        public override int ReadTimeout 
        {
            get 
            { 
                int constant = commTimeouts.ReadTotalTimeoutConstant;
 
                if (constant == infiniteTimeoutConst) return SerialPort.InfiniteTimeout;
                else return constant;
            }
            set 
            {
                if (value < 0 && value != SerialPort.InfiniteTimeout) 
                    throw new ArgumentOutOfRangeException("ReadTimeout", SR.GetString(SR.ArgumentOutOfRange_Timeout)); 
                if (_handle == null) InternalResources.FileNotOpen();
 
                int oldReadConstant = commTimeouts.ReadTotalTimeoutConstant;
                int oldReadInterval = commTimeouts.ReadIntervalTimeout;
                int oldReadMultipler = commTimeouts.ReadTotalTimeoutMultiplier;
 
                // NOTE: this logic should match what is in the constructor
                if (value == 0) { 
                    commTimeouts.ReadTotalTimeoutConstant   = 0; 
                    commTimeouts.ReadTotalTimeoutMultiplier = 0;
                    commTimeouts.ReadIntervalTimeout        = NativeMethods.MAXDWORD; 
                } else if (value == SerialPort.InfiniteTimeout) {
                    // SetCommTimeouts doesn't like a value of -1 for some reason, so
                    // we'll use -2(infiniteTimeoutConst) to represent infinite.
                    commTimeouts.ReadTotalTimeoutConstant   = infiniteTimeoutConst; 
                    commTimeouts.ReadTotalTimeoutMultiplier = NativeMethods.MAXDWORD;
                    commTimeouts.ReadIntervalTimeout        = NativeMethods.MAXDWORD; 
                } else { 
                    commTimeouts.ReadTotalTimeoutConstant   = value;
                    commTimeouts.ReadTotalTimeoutMultiplier = NativeMethods.MAXDWORD; 
                    commTimeouts.ReadIntervalTimeout        = NativeMethods.MAXDWORD;
                }

                if (UnsafeNativeMethods.SetCommTimeouts(_handle, ref commTimeouts) == false) 
                {
                    commTimeouts.ReadTotalTimeoutConstant = oldReadConstant; 
                    commTimeouts.ReadTotalTimeoutMultiplier = oldReadMultipler; 
                    commTimeouts.ReadIntervalTimeout = oldReadInterval;
                    InternalResources.WinIOError(); 
                }
            }
        }
 
        internal bool RtsEnable
        { 
            get { 
                int fRtsControl = GetDcbFlag(NativeMethods.FRTSCONTROL);
                if (fRtsControl == NativeMethods.RTS_CONTROL_HANDSHAKE) 
                    throw new InvalidOperationException(SR.GetString(SR.CantSetRtsWithHandshaking));

                return (fRtsControl == NativeMethods.RTS_CONTROL_ENABLE);
            } 
            set
            { 
                if ((handshake == Handshake.RequestToSend || handshake == Handshake.RequestToSendXOnXOff)) 
                    throw new InvalidOperationException(SR.GetString(SR.CantSetRtsWithHandshaking));
 
                if (value != rtsEnable) {
                    int fRtsControlOld = GetDcbFlag(NativeMethods.FRTSCONTROL);

                    rtsEnable = value; 
                    if(value)
                        SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_ENABLE); 
                    else 
                        SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_DISABLE);
 
                    if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false)
                    {
                        SetDcbFlag(NativeMethods.FRTSCONTROL, fRtsControlOld);
                        // set it back to the old value on a failure 
                        rtsEnable = !rtsEnable;
                        InternalResources.WinIOError(); 
                    } 

                    if (!UnsafeNativeMethods.EscapeCommFunction(_handle, value ? NativeMethods.SETRTS : NativeMethods.CLRRTS)) 
                        InternalResources.WinIOError();
                }
            }
        } 

        // StopBits represented in C# as StopBits enum type and in Win32 as an integer 1, 2, or 3. 
        internal StopBits StopBits 
        {
            /*get 
            {
                switch(dcb.StopBits)
                {
                    case NativeMethods.ONESTOPBIT: 
                        return StopBits.One;
                    case NativeMethods.ONE5STOPBITS: 
                        return StopBits.OnePointFive; 
                    case NativeMethods.TWOSTOPBITS:
                        return StopBits.Two; 
                    default:
                        Debug.Assert(true, "Invalid Stopbits value " + dcb.StopBits);
                        return StopBits.One;
 
                }
            } 
            */ 
            set
            { 
                Debug.Assert(!(value < StopBits.One || value > StopBits.OnePointFive), "An invalid value was passed to StopBits");

                byte nativeValue = 0;
                if (value == StopBits.One) nativeValue = (byte) NativeMethods.ONESTOPBIT; 
                else if (value == StopBits.OnePointFive) nativeValue = (byte) NativeMethods.ONE5STOPBITS;
                else nativeValue = (byte) NativeMethods.TWOSTOPBITS; 
 
                if(nativeValue != dcb.StopBits)
                { 
                    byte stopBitsOld = dcb.StopBits;
                    dcb.StopBits = nativeValue;

                    if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false) 
                    {
                        dcb.StopBits = stopBitsOld; 
                        InternalResources.WinIOError(); 
                    }
                } 
            }
        }

        // note: WriteTimeout must be either SerialPort.InfiniteTimeout or POSITIVE. 
        // a timeout of zero implies that every Write call throws an exception.
        public override int WriteTimeout 
        { 
            get
            { 
                int timeout = commTimeouts.WriteTotalTimeoutConstant;
                return (timeout == 0) ? SerialPort.InfiniteTimeout : timeout;
            }
            set 
            {
                if (value <= 0 && value != SerialPort.InfiniteTimeout) 
                    throw new ArgumentOutOfRangeException("WriteTimeout", SR.GetString(SR.ArgumentOutOfRange_WriteTimeout)); 
                if (_handle == null) InternalResources.FileNotOpen();
 
                int oldWriteConstant = commTimeouts.WriteTotalTimeoutConstant;
                commTimeouts.WriteTotalTimeoutConstant = ((value == SerialPort.InfiniteTimeout) ? 0 : value);

                if (UnsafeNativeMethods.SetCommTimeouts(_handle, ref commTimeouts) == false) 
                {
                    commTimeouts.WriteTotalTimeoutConstant = oldWriteConstant; 
                    InternalResources.WinIOError(); 
                }
            } 
        }


        // CDHolding, CtsHolding, DsrHolding query the current state of each of the carrier, the CTS pin, 
        // and the DSR pin, respectively. Read-only.
        // All will throw exceptions if the port is not open. 
        internal bool CDHolding 
        {
            get 
            {
                int pinStatus = 0;
                if (UnsafeNativeMethods.GetCommModemStatus(_handle, ref pinStatus) == false)
                    InternalResources.WinIOError(); 

                return (NativeMethods.MS_RLSD_ON & pinStatus) != 0; 
            } 
        }
 

        internal bool CtsHolding
        {
            get 
            {
                int pinStatus = 0; 
                if (UnsafeNativeMethods.GetCommModemStatus(_handle, ref pinStatus) == false) 
                    InternalResources.WinIOError();
                return (NativeMethods.MS_CTS_ON & pinStatus) != 0; 
            }

        }
 
        internal bool DsrHolding
        { 
            get 
            {
                int pinStatus = 0; 
                if (UnsafeNativeMethods.GetCommModemStatus(_handle, ref pinStatus) == false)
                    InternalResources.WinIOError();

                return (NativeMethods.MS_DSR_ON & pinStatus) != 0; 
            }
        } 
 

        // Fills comStat structure from an unmanaged function 
        // to determine the number of bytes waiting in the serial driver's internal receive buffer.
        internal int BytesToRead {
            get
            { 
                int errorCode = 0; // "ref" arguments need to have values, as opposed to "out" arguments
                if (UnsafeNativeMethods.ClearCommError(_handle, ref errorCode, ref comStat)  == false) 
                { 
                    InternalResources.WinIOError();
                } 
                return (int) comStat.cbInQue;
            }
        }
 
        // Fills comStat structure from an unmanaged function
        // to determine the number of bytes waiting in the serial driver's internal transmit buffer. 
        internal int BytesToWrite { 
            get
            { 
                int errorCode = 0; // "ref" arguments need to be set before method invocation, as opposed to "out" arguments
                if (UnsafeNativeMethods.ClearCommError(_handle, ref errorCode, ref comStat)  == false)
                    InternalResources.WinIOError();
                return (int) comStat.cbOutQue; 

            } 
        } 

        // -----------SECTION: constructor --------------------------* 

        // this method is used by SerialPort upon SerialStream's creation
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)] 
        internal SerialStream(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits, int readTimeout, int writeTimeout, Handshake handshake,
            bool dtrEnable, bool rtsEnable, bool discardNull, byte parityReplace) 
        { 

            int flags = UnsafeNativeMethods.FILE_FLAG_OVERLAPPED; 
            // disable async on win9x
            if (Environment.OSVersion.Platform == PlatformID.Win32Windows) {
                flags = UnsafeNativeMethods.FILE_ATTRIBUTE_NORMAL;
                isAsync = false; 
            }
 
            if ((portName == null) || !portName.StartsWith("COM", StringComparison.OrdinalIgnoreCase)) 
                throw new ArgumentException(SR.GetString(SR.Arg_InvalidSerialPort), "portName");
 
            //Error checking done in SerialPort.

            SafeFileHandle tempHandle = UnsafeNativeMethods.CreateFile("\\\\.\\" + portName,
                NativeMethods.GENERIC_READ | NativeMethods.GENERIC_WRITE, 
                0,    // comm devices must be opened w/exclusive-access
                IntPtr.Zero, // no security attributes 
                UnsafeNativeMethods.OPEN_EXISTING, // comm devices must use OPEN_EXISTING 
                flags,
                IntPtr.Zero  // hTemplate must be NULL for comm devices 
                );

            if (tempHandle.IsInvalid)
            { 
                InternalResources.WinIOError(portName);
            } 
 
            try {
                int fileType = UnsafeNativeMethods.GetFileType(tempHandle); 

                // Allowing FILE_TYPE_UNKNOWN for legitimate serial device such as USB to serial adapter device
                if ((fileType != UnsafeNativeMethods.FILE_TYPE_CHAR) && (fileType != UnsafeNativeMethods.FILE_TYPE_UNKNOWN))
                    throw new ArgumentException(SR.GetString(SR.Arg_InvalidSerialPort), "portName"); 

                _handle = tempHandle; 
 
                // set properties of the stream that exist as members in SerialStream
                this.portName = portName; 
                this.handshake = handshake;
                this.parityReplace = parityReplace;

                tempBuf = new byte[1];          // used in ReadByte() 

                // Fill COMMPROPERTIES struct, which has our maximum allowed baud rate. 
                // Call a serial specific API such as GetCommModemStatus which would fail 
                // in case the device is not a legitimate serial device. For instance,
                // some illegal FILE_TYPE_UNKNOWN device (or) "LPT1" on Win9x 
                // trying to pass for serial will be caught here. GetCommProperties works
                // fine for "LPT1" on Win9x, so that alone can't be relied here to
                // detect non serial devices.
 
                commProp = new UnsafeNativeMethods.COMMPROP();
                int pinStatus = 0; 
 
                if (!UnsafeNativeMethods.GetCommProperties(_handle, ref commProp)
                    || !UnsafeNativeMethods.GetCommModemStatus(_handle, ref pinStatus)) 
                {
                    // If the portName they have passed in is a FILE_TYPE_CHAR but not a serial port,
                    // for example "LPT1", this API will fail.  For this reason we handle the error message specially.
                    int errorCode = Marshal.GetLastWin32Error(); 
                    if ((errorCode == NativeMethods.ERROR_INVALID_PARAMETER) || (errorCode == NativeMethods.ERROR_INVALID_HANDLE))
                        throw new ArgumentException(SR.GetString(SR.Arg_InvalidSerialPortExtended), "portName"); 
                    else 
                        InternalResources.WinIOError(errorCode, string.Empty);
                } 
                if (commProp.dwMaxBaud != 0 && baudRate > commProp.dwMaxBaud)
                    throw new ArgumentOutOfRangeException("baudRate", SR.GetString(SR.Max_Baud, commProp.dwMaxBaud));

 
                comStat = new UnsafeNativeMethods.COMSTAT();
                // create internal DCB structure, initialize according to Platform SDK 
                // standard: ms-help://MS.MSNDNQTR.2002APR.1003/hardware/commun_965u.htm 
                dcb = new UnsafeNativeMethods.DCB();
 
                // set constant properties of the DCB
                InitializeDCB(baudRate, parity, dataBits, stopBits, discardNull);

                this.DtrEnable = dtrEnable; 

                // query and cache the initial RtsEnable value 
                // so that set_RtsEnable can do the (value != rtsEnable) optimization 
                this.rtsEnable = (GetDcbFlag(NativeMethods.FRTSCONTROL) == NativeMethods.RTS_CONTROL_ENABLE);
 
                // now set this.RtsEnable to the specified value.
                // Handshake takes precedence, this will be a nop if
                // handshake is either RequestToSend or RequestToSendXOnXOff
                if ((handshake != Handshake.RequestToSend && handshake != Handshake.RequestToSendXOnXOff)) 
                    this.RtsEnable = rtsEnable;
 
                // NOTE: this logic should match what is in the ReadTimeout property 
                if (readTimeout == 0) {
                    commTimeouts.ReadTotalTimeoutConstant   = 0; 
                    commTimeouts.ReadTotalTimeoutMultiplier = 0;
                    commTimeouts.ReadIntervalTimeout        = NativeMethods.MAXDWORD;
                } else if (readTimeout == SerialPort.InfiniteTimeout) {
                    // SetCommTimeouts doesn't like a value of -1 for some reason, so 
                    // we'll use -2(infiniteTimeoutConst) to represent infinite.
                    commTimeouts.ReadTotalTimeoutConstant   = infiniteTimeoutConst; 
                    commTimeouts.ReadTotalTimeoutMultiplier = NativeMethods.MAXDWORD; 
                    commTimeouts.ReadIntervalTimeout        = NativeMethods.MAXDWORD;
                } else { 
                    commTimeouts.ReadTotalTimeoutConstant   = readTimeout;
                    commTimeouts.ReadTotalTimeoutMultiplier = NativeMethods.MAXDWORD;
                    commTimeouts.ReadIntervalTimeout        = NativeMethods.MAXDWORD;
                } 

                commTimeouts.WriteTotalTimeoutMultiplier    = 0; 
                commTimeouts.WriteTotalTimeoutConstant      = ((writeTimeout == SerialPort.InfiniteTimeout) ? 0 : writeTimeout); 

                // set unmanaged timeout structure 
                if (UnsafeNativeMethods.SetCommTimeouts(_handle, ref commTimeouts) == false)
                {
                    InternalResources.WinIOError();
                } 

                if (isAsync) { 
                    if (!ThreadPool.BindHandle(_handle)) 
                    {
                        throw new IOException(SR.GetString(SR.IO_BindHandleFailed)); 
                    }
                }

                // monitor all events except TXEMPTY 
                UnsafeNativeMethods.SetCommMask(_handle, NativeMethods.ALL_EVENTS);
 
                // prep. for starting event cycle. 
                eventRunner = new EventLoopRunner(this);
                Thread eventLoopThread = new Thread(new ThreadStart(eventRunner.WaitForCommEvent)); 
                eventLoopThread.IsBackground = true;
                eventLoopThread.Start();

            } 
            catch  {
                // if there are any exceptions after the call to CreateFile, we need to be sure to close the 
                // handle before we let them continue up. 
                tempHandle.Close();
                _handle = null; 
                throw;
            }
        }
 
        ~SerialStream()
        { 
            Dispose(false); 
        }
 
        protected override void Dispose(bool disposing)
        {
            // Signal the other side that we're closing.  Should do regardless of whether we've called
            // Close() or not Dispose() 
            if (_handle != null && !_handle.IsInvalid) {
                try { 
 
                    eventRunner.endEventLoop = true;
 
                    Thread.MemoryBarrier();

                    // turn off all events and signal WaitCommEvent
                    UnsafeNativeMethods.SetCommMask(_handle, 0); 
                    if(!UnsafeNativeMethods.EscapeCommFunction(_handle, NativeMethods.CLRDTR))
                    { 
                        // should not happen 
                        InternalResources.WinIOError();
                    } 

                    if (!_handle.IsClosed)
                        Flush();
 
                    eventRunner.waitCommEventWaitHandle.Set();
                    DiscardInBuffer(); 
                    DiscardOutBuffer(); 

                    if (disposing && eventRunner != null) { 
                        // now we need to wait for the event loop to tell us it's done.  Without this we could get into a ---- where the
                        // event loop kept the port open even after Dispose ended.
                        eventRunner.eventLoopEndedSignal.WaitOne();
                        eventRunner.eventLoopEndedSignal.Close(); 
                        eventRunner.waitCommEventWaitHandle.Close();
                    } 
                } 
                finally {
                    // If we are disposing synchronize closing with raising SerialPort events 
                    if (disposing) {
                        lock (this) {
                            _handle.Close();
                            _handle = null; 
                        }
                    } 
                    else { 
                        _handle.Close();
                        _handle = null; 
                    }
                    base.Dispose(disposing);
                }
 
            }
        } 
 
        // -----SECTION: all public methods ------------------*
 
        // User-accessible async read method.  Returns SerialStreamAsyncResult : IAsyncResult
        [HostProtection(ExternalThreading=true)]
        public override IAsyncResult BeginRead(byte[] array, int offset,int numBytes, AsyncCallback userCallback, object stateObject)
        { 
            if (array==null)
                throw new ArgumentNullException("array"); 
            if (offset < 0) 
                throw new ArgumentOutOfRangeException("offset", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired));
            if (numBytes < 0) 
                throw new ArgumentOutOfRangeException("numBytes", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired));
            if (array.Length - offset < numBytes)
                throw new ArgumentException(SR.GetString(SR.Argument_InvalidOffLen));
            if (_handle == null) InternalResources.FileNotOpen(); 

            int oldtimeout = ReadTimeout; 
            ReadTimeout = SerialPort.InfiniteTimeout; 
            IAsyncResult result;
            try { 
                if (!isAsync)
                    result = base.BeginRead(array, offset, numBytes, userCallback, stateObject);
                else
                    result = BeginReadCore(array, offset, numBytes, userCallback, stateObject); 

            } 
            finally { 
                ReadTimeout = oldtimeout;
            } 
            return result;
        }

        // User-accessible async write method.  Returns SerialStreamAsyncResult : IAsyncResult 
        // Throws an exception if port is in break state.
        [HostProtection(ExternalThreading=true)] 
        public override IAsyncResult BeginWrite(byte[] array, int offset, int numBytes, 
            AsyncCallback userCallback, object stateObject)
        { 
            if (inBreak)
                throw new InvalidOperationException(SR.GetString(SR.In_Break_State));
            if (array==null)
                throw new ArgumentNullException("array"); 
            if (offset < 0)
                throw new ArgumentOutOfRangeException("offset", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired)); 
            if (numBytes < 0) 
                throw new ArgumentOutOfRangeException("numBytes", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired));
            if (array.Length - offset < numBytes) 
                throw new ArgumentException(SR.GetString(SR.Argument_InvalidOffLen));
            if (_handle == null) InternalResources.FileNotOpen();

            int oldtimeout = WriteTimeout; 
            WriteTimeout = SerialPort.InfiniteTimeout;
            IAsyncResult result; 
            try { 
                if (!isAsync)
                    result = base.BeginWrite(array, offset, numBytes, userCallback, stateObject); 
                else
                    result = BeginWriteCore(array, offset, numBytes, userCallback, stateObject);
            }
            finally { 
                WriteTimeout = oldtimeout;
            } 
            return result; 
        }
 
        // Uses Win32 method to dump out the receive buffer; analagous to MSComm's "InBufferCount = 0"
        internal void DiscardInBuffer()
        {
 
            if (UnsafeNativeMethods.PurgeComm(_handle, NativeMethods.PURGE_RXCLEAR | NativeMethods.PURGE_RXABORT) == false)
                InternalResources.WinIOError(); 
        } 

        // Uses Win32 method to dump out the xmit buffer; analagous to MSComm's "OutBufferCount = 0" 
        internal void DiscardOutBuffer()
        {
            if (UnsafeNativeMethods.PurgeComm(_handle, NativeMethods.PURGE_TXCLEAR | NativeMethods.PURGE_TXABORT) == false)
                InternalResources.WinIOError(); 
        }
 
        // Async companion to BeginRead. 
        // Note, assumed IAsyncResult argument is of derived type SerialStreamAsyncResult,
        // and throws an exception if untrue. 
        public unsafe override int EndRead(IAsyncResult asyncResult)
        {
            if (!isAsync)
                return base.EndRead(asyncResult); 

            if (asyncResult==null) 
                throw new ArgumentNullException("asyncResult"); 

            SerialStreamAsyncResult afsar = asyncResult as SerialStreamAsyncResult; 
            if (afsar==null || afsar._isWrite)
                InternalResources.WrongAsyncResult();

            // This sidesteps race conditions, avoids memory corruption after freeing the 
            // NativeOverlapped class or GCHandle twice.
            if (1 == Interlocked.CompareExchange(ref afsar._EndXxxCalled, 1, 0)) 
                InternalResources.EndReadCalledTwice(); 

            bool failed = false; 

            // Obtain the WaitHandle, but don't use public property in case we
            // delay initialize the manual reset event in the future.
            WaitHandle wh = afsar._waitHandle; 
            if (wh != null)
            { 
                // We must block to ensure that AsyncFSCallback has completed, 
                // and we should close the WaitHandle in here.
                try { 
                    wh.WaitOne();
                    Debug.Assert(afsar._isComplete == true, "SerialStream::EndRead - AsyncFSCallback didn't set _isComplete to true!");

                    // InfiniteTimeout is not something native to the underlying serial device, 
                    // we specify the timeout to be a very large value (MAXWORD-1) to achieve
                    // an infinite timeout illusion. 
 
                    // I'm not sure what we can do here after an asyn operation with infinite
                    // timeout returns with no data. From a purist point of view we should 
                    // somehow restart the read operation but we are not in a position to do so
                    // (and frankly that may not necessarily be the right thing to do here)
                    // I think the best option in this (almost impossible to run into) situation
                    // is to throw some sort of IOException. 

                    if ((afsar._numBytes == 0) && (ReadTimeout == SerialPort.InfiniteTimeout) && (afsar._errorCode == 0)) 
                      failed = true; 
                }
                finally { 
                    wh.Close();
                }
            }
 
            // Free memory, GC handles.
            NativeOverlapped* overlappedPtr = afsar._overlapped; 
            if (overlappedPtr != null) 
                Overlapped.Free(overlappedPtr);
            afsar.UnpinBuffer(); 

            // Check for non-timeout errors during the read.
            if (afsar._errorCode != 0)
                InternalResources.WinIOError(afsar._errorCode, portName); 

            if (failed) 
                throw new IOException(SR.GetString(SR.IO_OperationAborted)); 

            return afsar._numBytes; 
        }

        // Async companion to BeginWrite.
        // Note, assumed IAsyncResult argument is of derived type SerialStreamAsyncResult, 
        // and throws an exception if untrue.
        // Also fails if called in port's break state. 
        public unsafe override void EndWrite(IAsyncResult asyncResult) { 
            if (!isAsync) {
                base.EndWrite(asyncResult); 
                return;
            }

            if (inBreak) 
                throw new InvalidOperationException(SR.GetString(SR.In_Break_State));
            if (asyncResult==null) 
                throw new ArgumentNullException("asyncResult"); 

            SerialStreamAsyncResult afsar = asyncResult as SerialStreamAsyncResult; 
            if (afsar==null || !afsar._isWrite)
                InternalResources.WrongAsyncResult();

            // This sidesteps race conditions, avoids memory corruption after freeing the 
            // NativeOverlapped class or GCHandle twice.
            if (1 == Interlocked.CompareExchange(ref afsar._EndXxxCalled, 1, 0)) 
                InternalResources.EndWriteCalledTwice(); 

            // Obtain the WaitHandle, but don't use public property in case we 
            // delay initialize the manual reset event in the future.
            WaitHandle wh = afsar._waitHandle;
            if (wh != null)
            { 
                // We must block to ensure that AsyncFSCallback has completed,
                // and we should close the WaitHandle in here. 
                try { 
                    wh.WaitOne();
                    Debug.Assert(afsar._isComplete == true, "SerialStream::EndWrite - AsyncFSCallback didn't set _isComplete to true!"); 
                }
                finally {
                    wh.Close();
                } 
            }
 
            // Free memory, GC handles. 
            NativeOverlapped* overlappedPtr = afsar._overlapped;
            if (overlappedPtr != null) 
                Overlapped.Free(overlappedPtr);
            afsar.UnpinBuffer();

            // Now check for any error during the write. 
            if (afsar._errorCode != 0)
                InternalResources.WinIOError(afsar._errorCode, portName); 
 
            // Number of bytes written is afsar._numBytes.
        } 

        // Flush dumps the contents of the serial driver's internal read and write buffers.
        // We actually expose the functionality for each, but fulfilling Stream's contract
        // requires a Flush() method.  Fails if handle closed. 
        // Note: Serial driver's write buffer is *already* attempting to write it, so we can only wait until it finishes.
        public override void Flush() 
        { 
            if (_handle == null) throw new ObjectDisposedException(SR.GetString(SR.Port_not_open));
            UnsafeNativeMethods.FlushFileBuffers(_handle); 
        }

        // Blocking read operation, returning the number of bytes read from the stream.
 
        public override int Read([In, Out] byte[] array, int offset, int count)
        { 
            return Read(array, offset, count, ReadTimeout); 
        }
 
        internal unsafe int Read([In, Out] byte[] array, int offset, int count, int timeout)
        {
            if (array==null)
                throw new ArgumentNullException("array", SR.GetString(SR.ArgumentNull_Buffer)); 
            if (offset < 0)
                throw new ArgumentOutOfRangeException("offset", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired)); 
            if (count < 0) 
                throw new ArgumentOutOfRangeException("count", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired));
            if (array.Length - offset < count) 
                throw new ArgumentException(SR.GetString(SR.Argument_InvalidOffLen));
            if (count == 0) return 0; // return immediately if no bytes requested; no need for overhead.

            Debug.Assert(timeout == SerialPort.InfiniteTimeout || timeout >= 0, "Serial Stream Read - called with timeout " + timeout); 

            // Check to see we have no handle-related error, since the port's always supposed to be open. 
            if (_handle == null) InternalResources.FileNotOpen(); 

            int numBytes = 0; 
            int hr;
            if (isAsync) {
                IAsyncResult result = BeginReadCore(array, offset, count, null, null);
                numBytes = EndRead(result); 
            }
            else { 
                numBytes = ReadFileNative(array, offset, count, null, out hr); 
                if (numBytes == -1) {
                    InternalResources.WinIOError(); 
                }
            }

            if (numBytes == 0) 
                throw new TimeoutException();
 
            return numBytes; 
        }
 
        public override int ReadByte()
        {
            return ReadByte(ReadTimeout);
        } 

        internal unsafe int ReadByte(int timeout) 
        { 
            if (_handle == null) InternalResources.FileNotOpen();
 
            int numBytes = 0;
            int hr;
            if (isAsync) {
                IAsyncResult result = BeginReadCore(tempBuf, 0, 1, null, null); 
                numBytes = EndRead(result);
            } 
            else { 
                numBytes = ReadFileNative(tempBuf, 0, 1, null, out hr);
                if (numBytes == -1) { 
                    InternalResources.WinIOError();
                }
            }
 
            if (numBytes == 0)
                throw new TimeoutException(); 
            else 
                return tempBuf[0];
        } 

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException(SR.GetString(SR.NotSupported_UnseekableStream)); 
        }
 
        public override void SetLength(long value) 
        {
            throw new NotSupportedException(SR.GetString(SR.NotSupported_UnseekableStream)); 
        }

        internal void SetBufferSizes(int readBufferSize, int writeBufferSize) {
            if (_handle == null) InternalResources.FileNotOpen(); 

            if (!UnsafeNativeMethods.SetupComm(_handle, readBufferSize, writeBufferSize)) 
                InternalResources.WinIOError(); 
        }
 
        public override void Write(byte[] array, int offset, int count)
        {
            Write(array, offset, count, WriteTimeout);
        } 

        internal unsafe void Write(byte[] array, int offset, int count, int timeout) 
        { 

            if (inBreak) 
                throw new InvalidOperationException(SR.GetString(SR.In_Break_State));
            if (array==null)
                throw new ArgumentNullException("buffer", SR.GetString(SR.ArgumentNull_Array));
            if (offset < 0) 
                throw new ArgumentOutOfRangeException("offset", SR.GetString(SR.ArgumentOutOfRange_NeedPosNum));
            if (count < 0) 
                throw new ArgumentOutOfRangeException("count", SR.GetString(SR.ArgumentOutOfRange_NeedPosNum)); 
            if (count == 0) return; // no need to expend overhead in creating asyncResult, etc.
            if (array.Length - offset < count) 
                throw new ArgumentException("count",SR.GetString(SR.ArgumentOutOfRange_OffsetOut));
            Debug.Assert(timeout == SerialPort.InfiniteTimeout || timeout >= 0, "Serial Stream Write - write timeout is " + timeout);

            // check for open handle, though the port is always supposed to be open 
            if (_handle == null) InternalResources.FileNotOpen();
 
            int numBytes; 
            int hr;
            if (isAsync) { 
                IAsyncResult result = BeginWriteCore(array, offset, count, null, null);
                EndWrite(result);

                SerialStreamAsyncResult afsar = result as SerialStreamAsyncResult; 
                Debug.Assert(afsar != null, "afsar should be a SerialStreamAsyncResult and should not be null");
                numBytes = afsar._numBytes; 
            } 
            else {
                numBytes = WriteFileNative(array, offset, count, null, out hr); 
                if (numBytes == -1) {

                    // This is how writes timeout on Win9x.
                    if (hr == NativeMethods.ERROR_COUNTER_TIMEOUT) 
                        throw new TimeoutException(SR.GetString(SR.Write_timed_out));
 
                    InternalResources.WinIOError(); 
                }
            } 

            if (numBytes == 0)
                throw new TimeoutException(SR.GetString(SR.Write_timed_out));
 
        }
 
        // use default timeout as argument to WriteByte override with timeout arg 
        public override void WriteByte(byte value)
        { 
            WriteByte(value, WriteTimeout);
        }

        internal unsafe void WriteByte(byte value, int timeout) 
        {
            if (inBreak) 
                throw new InvalidOperationException(SR.GetString(SR.In_Break_State)); 

            if (_handle == null) InternalResources.FileNotOpen(); 
            tempBuf[0] = value;


            int numBytes; 
            int hr;
            if (isAsync) { 
                IAsyncResult result = BeginWriteCore(tempBuf, 0, 1, null, null); 
                EndWrite(result);
 
                SerialStreamAsyncResult afsar = result as SerialStreamAsyncResult;
                Debug.Assert(afsar != null, "afsar should be a SerialStreamAsyncResult and should not be null");
                numBytes = afsar._numBytes;
            } 
            else {
                numBytes = WriteFileNative(tempBuf, 0, 1, null, out hr); 
                if (numBytes == -1) { 
                    // This is how writes timeout on Win9x.
                    if (Marshal.GetLastWin32Error() == NativeMethods.ERROR_COUNTER_TIMEOUT) 
                        throw new TimeoutException(SR.GetString(SR.Write_timed_out));

                    InternalResources.WinIOError();
                } 
            }
 
            if (numBytes == 0) 
                throw new TimeoutException(SR.GetString(SR.Write_timed_out));
 
            return;
        }

 

        // --------SUBSECTION: internal-use methods ----------------------* 
        // ------ internal DCB-supporting methods ------- * 

        // Initializes unmananged DCB struct, to be called after opening communications resource. 
        // assumes we have already: baudRate, parity, dataBits, stopBits
        // should only be called in SerialStream(...)
        private void InitializeDCB(int baudRate, Parity parity, int dataBits, StopBits stopBits, bool discardNull)
        { 

            // first get the current dcb structure setup 
            if (UnsafeNativeMethods.GetCommState(_handle, ref dcb) == false) 
            {
                InternalResources.WinIOError(); 
            }
            dcb.DCBlength = (uint) System.Runtime.InteropServices.Marshal.SizeOf(dcb);

            // set parameterized properties 
            dcb.BaudRate = (uint) baudRate;
            dcb.ByteSize = (byte) dataBits; 
 

            switch (stopBits) 
            {
                case StopBits.One:
                    dcb.StopBits = NativeMethods.ONESTOPBIT;
                    break; 
                case StopBits.OnePointFive:
                    dcb.StopBits = NativeMethods.ONE5STOPBITS; 
                    break; 
                case StopBits.Two:
                    dcb.StopBits = NativeMethods.TWOSTOPBITS; 
                    break;
                default:
                    Debug.Assert(false, "Invalid value for stopBits");
                    break; 
            }
 
            dcb.Parity = (byte) parity; 
            // SetDcbFlag, GetDcbFlag expose access to each of the relevant bits of the 32-bit integer
            // storing all flags of the DCB.  C# provides no direct means of manipulating bit fields, so 
            // this is the solution.
            SetDcbFlag(NativeMethods.FPARITY, ((parity == Parity.None)  ?  0  :  1));

            SetDcbFlag(NativeMethods.FBINARY, 1);   // always true for communications resources 

            // set DCB fields implied by default and the arguments given. 
            // Boolean fields in C# must become 1, 0 to properly set the bit flags in the unmanaged DCB struct 

            SetDcbFlag(NativeMethods.FOUTXCTSFLOW, ((handshake == Handshake.RequestToSend || 
                handshake == Handshake.RequestToSendXOnXOff) ? 1 : 0));
            // SetDcbFlag(NativeMethods.FOUTXDSRFLOW, (dsrTimeout != 0L) ? 1 : 0);
            SetDcbFlag(NativeMethods.FOUTXDSRFLOW, 0); // dsrTimeout is always set to 0.
            SetDcbFlag(NativeMethods.FDTRCONTROL, NativeMethods.DTR_CONTROL_DISABLE); 
            SetDcbFlag(NativeMethods.FDSRSENSITIVITY, 0); // this should remain off
            SetDcbFlag(NativeMethods.FINX, (handshake == Handshake.XOnXOff || handshake == Handshake.RequestToSendXOnXOff) ? 1 : 0); 
            SetDcbFlag(NativeMethods.FOUTX,(handshake == Handshake.XOnXOff || handshake == Handshake.RequestToSendXOnXOff) ? 1 : 0); 

            // if no parity, we have no error character (i.e. ErrorChar = '\0' or null character) 
            if (parity != Parity.None)
            {
                SetDcbFlag(NativeMethods.FERRORCHAR, (parityReplace != '\0') ? 1 : 0);
                dcb.ErrorChar = parityReplace; 
            }
            else 
            { 
                SetDcbFlag(NativeMethods.FERRORCHAR, 0);
                dcb.ErrorChar = (byte) '\0'; 
            }

            // this method only runs once in the constructor, so we only have the default value to use.
            // Later the user may change this via the NullDiscard property. 
            SetDcbFlag(NativeMethods.FNULL, discardNull ? 1 : 0);
 
 
            // Setting RTS control, which is RTS_CONTROL_HANDSHAKE if RTS / RTS-XOnXOff handshaking
            // used, RTS_ENABLE (RTS pin used during operation) if rtsEnable true but XOnXoff / No handshaking 
            // used, and disabled otherwise.
            if ((handshake == Handshake.RequestToSend ||
                handshake == Handshake.RequestToSendXOnXOff))
            { 
                SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_HANDSHAKE);
            } 
            else if (GetDcbFlag(NativeMethods.FRTSCONTROL) == NativeMethods.RTS_CONTROL_HANDSHAKE) 
            {
                SetDcbFlag(NativeMethods.FRTSCONTROL, NativeMethods.RTS_CONTROL_DISABLE); 
            }

            dcb.XonChar = NativeMethods.DEFAULTXONCHAR;             // may be exposed later but for now, constant
            dcb.XoffChar = NativeMethods.DEFAULTXOFFCHAR; 

            // minimum number of bytes allowed in each buffer before flow control activated 
            // heuristically, this has been set at 1/4 of the buffer size 
            dcb.XonLim = dcb.XoffLim = (ushort) (commProp.dwCurrentRxQueue / 4);
 
            dcb.EofChar = NativeMethods.EOFCHAR;

            //OLD MSCOMM: dcb.EvtChar = (byte) 0;
            // now changed to make use of RXFlag WaitCommEvent event => Eof WaitForCommEvent event 
            dcb.EvtChar = NativeMethods.EOFCHAR;
 
            // set DCB structure 
            if (UnsafeNativeMethods.SetCommState(_handle, ref dcb) == false)
            { 
                InternalResources.WinIOError();
            }
        }
 
        // Here we provide a method for getting the flags of the Device Control Block structure dcb
        // associated with each instance of SerialStream, i.e. this method gets myStream.dcb.Flags 
        // Flags are any of the constants in NativeMethods such as FBINARY, FDTRCONTROL, etc. 
        internal int GetDcbFlag(int whichFlag)
        { 
            uint mask;

            Debug.Assert(whichFlag >= NativeMethods.FBINARY && whichFlag <= NativeMethods.FDUMMY2, "GetDcbFlag needs to fit into enum!");
 
            if (whichFlag == NativeMethods.FDTRCONTROL || whichFlag == NativeMethods.FRTSCONTROL)
            { 
                mask = 0x3; 
            }
            else if (whichFlag == NativeMethods.FDUMMY2) 
            {
                mask = 0x1FFFF;
            }
            else 
            {
                mask = 0x1; 
            } 
            uint result = dcb.Flags & (mask << whichFlag);
            return (int) (result >> whichFlag); 
        }

        // Since C# applications have to provide a workaround for accessing and setting bitfields in unmanaged code,
        // here we provide methods for getting and setting the Flags field of the Device Control Block structure dcb 
        // associated with each instance of SerialStream, i.e. this method sets myStream.dcb.Flags
        // Flags are any of the constants in NativeMethods such as FBINARY, FDTRCONTROL, etc. 
        internal void SetDcbFlag(int whichFlag, int setting) 
        {
            uint mask; 
            setting = setting << whichFlag;

            Debug.Assert(whichFlag >= NativeMethods.FBINARY && whichFlag <= NativeMethods.FDUMMY2, "SetDcbFlag needs to fit into enum!");
 
            if (whichFlag == NativeMethods.FDTRCONTROL || whichFlag == NativeMethods.FRTSCONTROL)
            { 
                mask = 0x3; 
            }
            else if (whichFlag == NativeMethods.FDUMMY2) 
            {
                mask = 0x1FFFF;
            }
            else 
            {
                mask = 0x1; 
            } 

            // clear the region 
            dcb.Flags &= ~(mask << whichFlag);

            // set the region
            dcb.Flags |= ((uint) setting); 
        }
 
        // ----SUBSECTION: internal methods supporting public read/write methods-------* 

        unsafe private SerialStreamAsyncResult BeginReadCore(byte[] array, int offset, int numBytes, AsyncCallback userCallback, Object stateObject) 
        {

            // Create and store async stream class library specific data in the
            // async result 
            SerialStreamAsyncResult asyncResult = new SerialStreamAsyncResult();
            asyncResult._userCallback = userCallback; 
            asyncResult._userStateObject = stateObject; 
            asyncResult._isWrite = false;
 
            // For Synchronous IO, I could go with either a callback and using
            // the managed Monitor class, or I could create a handle and wait on it.
            ManualResetEvent waitHandle = new ManualResetEvent(false);
            asyncResult._waitHandle = waitHandle; 

            // Create a managed overlapped class 
            // We will set the file offsets later 
            Overlapped overlapped = new Overlapped(0, 0, IntPtr.Zero, asyncResult);
 
            // Pack the Overlapped class, and store it in the async result
            NativeOverlapped* intOverlapped = overlapped.Pack(IOCallback, null);
            asyncResult._overlapped = intOverlapped;
 
            // Keep the array in one location in memory until the OS writes the
            // relevant data into the array.  Free GCHandle later. 
            asyncResult.PinBuffer(array); 

            // queue an async ReadFile operation and pass in a packed overlapped 
            //int r = ReadFile(_handle, array, numBytes, null, intOverlapped);
            int hr = 0;
            int r = ReadFileNative(array, offset, numBytes,
             intOverlapped, out hr); 

            // ReadFile, the OS version, will return 0 on failure.  But 
            // my ReadFileNative wrapper returns -1.  My wrapper will return 
            // the following:
            // On error, r==-1. 
            // On async requests that are still pending, r==-1 w/ hr==ERROR_IO_PENDING
            // on async requests that completed sequentially, r==0
            // Note that you will NEVER RELIABLY be able to get the number of bytes
            // read back from this call when using overlapped structures!  You must 
            // not pass in a non-null lpNumBytesRead to ReadFile when using
            // overlapped structures! 
            if (r==-1) 
            {
                if (hr != NativeMethods.ERROR_IO_PENDING) 
                {
                    if (hr == NativeMethods.ERROR_HANDLE_EOF)
                        InternalResources.EndOfFile();
                    else 
                        InternalResources.WinIOError(hr, String.Empty);
                } 
            } 

            return asyncResult; 
        }

        unsafe private SerialStreamAsyncResult BeginWriteCore(byte[] array, int offset, int numBytes, AsyncCallback userCallback, Object stateObject)
        { 
            // Create and store async stream class library specific data in the
            // async result 
            SerialStreamAsyncResult asyncResult = new SerialStreamAsyncResult(); 
            asyncResult._userCallback = userCallback;
            asyncResult._userStateObject = stateObject; 
            asyncResult._isWrite = true;

            // For Synchronous IO, I could go with either a callback and using
            // the managed Monitor class, or I could create a handle and wait on it. 
            ManualResetEvent waitHandle = new ManualResetEvent(false);
            asyncResult._waitHandle = waitHandle; 
 
            // Create a managed overlapped class
            // We will set the file offsets later 
            Overlapped overlapped = new Overlapped(0, 0, IntPtr.Zero, asyncResult);

            // Pack the Overlapped class, and store it in the async result
            NativeOverlapped* intOverlapped = overlapped.Pack(IOCallback, null); 
            asyncResult._overlapped = intOverlapped;
 
 
            // Keep the array in one location in memory until the OS reads the
            // relevant data from the array.  Free GCHandle later. 
            asyncResult.PinBuffer(array);

            int hr = 0;
            // queue an async WriteFile operation and pass in a packed overlapped 
            int r = WriteFileNative(array, offset, numBytes, intOverlapped, out hr);
 
            // WriteFile, the OS version, will return 0 on failure.  But 
            // my WriteFileNative wrapper returns -1.  My wrapper will return
            // the following: 
            // On error, r==-1.
            // On async requests that are still pending, r==-1 w/ hr==ERROR_IO_PENDING
            // On async requests that completed sequentially, r==0
            // Note that you will NEVER RELIABLY be able to get the number of bytes 
            // written back from this call when using overlapped IO!  You must
            // not pass in a non-null lpNumBytesWritten to WriteFile when using 
            // overlapped structures! 
            if (r==-1)
            { 
                if (hr != NativeMethods.ERROR_IO_PENDING)
                {

                    if (hr == NativeMethods.ERROR_HANDLE_EOF) 
                        InternalResources.EndOfFile();
                    else 
                        InternalResources.WinIOError(hr, String.Empty); 
                }
            } 
            return asyncResult;
        }

 
        // Internal method, wrapping the PInvoke to ReadFile().
        private unsafe int ReadFileNative(byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int hr) 
        { 

            // Don't corrupt memory when multiple threads are erroneously writing 
            // to this stream simultaneously.
            if (bytes.Length - offset < count)
                throw new IndexOutOfRangeException(SR.GetString(SR.IndexOutOfRange_IORaceCondition));
 
            // You can't use the fixed statement on an array of length 0.
            if (bytes.Length==0) 
            { 
                hr = 0;
                return 0; 
            }

            int r = 0;
            int numBytesRead = 0; 

            fixed(byte* p = bytes) 
            { 
                if (isAsync)
                    r = UnsafeNativeMethods.ReadFile(_handle, p + offset, count, IntPtr.Zero, overlapped); 
                else
                    r = UnsafeNativeMethods.ReadFile(_handle, p + offset, count, out numBytesRead, IntPtr.Zero);
            }
 
            if (r==0)
            { 
                hr = Marshal.GetLastWin32Error(); 

                // Note: we should never silently ---- an error here without some 
                // extra work.  We must make sure that BeginReadCore won't return an
                // IAsyncResult that will cause EndRead to block, since the OS won't
                // call AsyncFSCallback for us.
 
                // For invalid handles, detect the error and mark our handle
                // as closed to give slightly better error messages.  Also 
                // help ensure we avoid handle recycling bugs. 
                if (hr == NativeMethods.ERROR_INVALID_HANDLE)
                    _handle.SetHandleAsInvalid(); 

                return -1;
            }
            else 
                hr = 0;
            return numBytesRead; 
        } 

        private unsafe int WriteFileNative(byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int hr) 
        {

            // Don't corrupt memory when multiple threads are erroneously writing
            // to this stream simultaneously.  (Note that the OS is reading from 
            // the array we pass to WriteFile, but if we read beyond the end and
            // that memory isn't allocated, we could get an AV.) 
            if (bytes.Length - offset < count) 
                throw new IndexOutOfRangeException(SR.GetString(SR.IndexOutOfRange_IORaceCondition));
 
            // You can't use the fixed statement on an array of length 0.
            if (bytes.Length==0)
            {
                hr = 0; 
                return 0;
            } 
 
            int numBytesWritten = 0;
            int r = 0; 

            fixed(byte* p = bytes)
            {
                if (isAsync) 
                    r = UnsafeNativeMethods.WriteFile(_handle, p + offset, count, IntPtr.Zero, overlapped);
                else 
                    r = UnsafeNativeMethods.WriteFile(_handle, p + offset, count, out numBytesWritten, IntPtr.Zero); 
            }
 
            if (r==0)
            {
                hr = Marshal.GetLastWin32Error();
                // Note: we should never silently ---- an error here without some 
                // extra work.  We must make sure that BeginWriteCore won't return an
                // IAsyncResult that will cause EndWrite to block, since the OS won't 
                // call AsyncFSCallback for us. 

                // For invalid handles, detect the error and mark our handle 
                // as closed to give slightly better error messages.  Also
                // help ensure we avoid handle recycling bugs.
                if (hr == NativeMethods.ERROR_INVALID_HANDLE)
                    _handle.SetHandleAsInvalid(); 

                return -1; 
            } 
            else
                hr = 0; 
            return numBytesWritten;
        }

        // ----SUBSECTION: internal methods supporting events/async operation------* 

        // This is a the callback prompted when a thread completes any async I/O operation. 
        unsafe private static void AsyncFSCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped) 
        {
            // Unpack overlapped 
            Overlapped overlapped = Overlapped.Unpack(pOverlapped);

            // Extract async the result from overlapped structure
            SerialStreamAsyncResult asyncResult = 
                (SerialStreamAsyncResult)overlapped.AsyncResult;
            asyncResult._numBytes = (int)numBytes; 
 
            asyncResult._errorCode = (int)errorCode;
 
            // Call the user-provided callback.  Note that it can and often should
            // call EndRead or EndWrite.  There's no reason to use an async
            // delegate here - we're already on a threadpool thread.
            // Note the IAsyncResult's completedSynchronously property must return 
            // false here, saying the user callback was called on another thread.
            asyncResult._completedSynchronously = false; 
            asyncResult._isComplete = true; 

            // The OS does not signal this event.  We must do it ourselves. 
            // But don't close it if the user callback called EndXxx,
            // which then closed the manual reset event already.
            ManualResetEvent wh = asyncResult._waitHandle;
            if (wh != null) { 
                bool r = wh.Set();
                if (!r) InternalResources.WinIOError(); 
            } 

            AsyncCallback userCallback = asyncResult._userCallback; 
            if (userCallback != null)
                userCallback(asyncResult);
        }
 

        // ----SECTION: internal classes --------* 
 
        internal sealed class EventLoopRunner {
            private WeakReference streamWeakReference; 
            internal ManualResetEvent eventLoopEndedSignal = new ManualResetEvent(false);
            internal ManualResetEvent waitCommEventWaitHandle = new ManualResetEvent(false);
            private SafeFileHandle handle = null;
            private bool isAsync; 
            internal bool endEventLoop;
            private int eventsOccurred; 
 
            WaitCallback callErrorEvents;
            WaitCallback callReceiveEvents; 
            WaitCallback callPinEvents;
            IOCompletionCallback freeNativeOverlappedCallback;

#if DEBUG 
            string portName;
#endif 
            internal unsafe EventLoopRunner(SerialStream stream) { 
                handle = stream._handle;
                streamWeakReference = new WeakReference(stream); 

                callErrorEvents = new WaitCallback(CallErrorEvents);
                callReceiveEvents = new WaitCallback(CallReceiveEvents );
                callPinEvents = new WaitCallback(CallPinEvents); 
                freeNativeOverlappedCallback = new IOCompletionCallback(FreeNativeOverlappedCallback);
                isAsync = stream.isAsync; 
#if DEBUG 
                portName = stream.portName;
#endif 
            }

            internal bool ShutdownLoop {
                get { 
                    return endEventLoop;
                } 
            } 

            // This is the blocking method that waits for an event to occur.  It wraps the SDK's WaitCommEvent function. 
            internal unsafe void WaitForCommEvent()
            {
                int unused = 0;
                bool doCleanup = false; 
                NativeOverlapped* intOverlapped = null;
                while (!ShutdownLoop) { 
                    SerialStreamAsyncResult asyncResult = null; 
                    if (isAsync) {
                        asyncResult = new SerialStreamAsyncResult(); 
                        asyncResult._userCallback = null;
                        asyncResult._userStateObject = null;
                        asyncResult._isWrite = false;
 
                        // we're going to use _numBytes for something different in this loop.  In this case, both
                        // freeNativeOverlappedCallback and this thread will decrement that value.  Whichever one decrements it 
                        // to zero will be the one to free the native overlapped.  This guarantees the overlapped gets freed 
                        // after both the callback and GetOverlappedResult have had a chance to use it.
                        asyncResult._numBytes = 2; 
                        asyncResult._waitHandle = waitCommEventWaitHandle;

                        waitCommEventWaitHandle.Reset();
                        Overlapped overlapped = new Overlapped(0, 0, waitCommEventWaitHandle.SafeWaitHandle.DangerousGetHandle(), asyncResult); 
                        // Pack the Overlapped class, and store it in the async result
                        intOverlapped = overlapped.Pack(freeNativeOverlappedCallback, null); 
                    } 

                    fixed (int* eventsOccurredPtr = &eventsOccurred) { 

                    if (UnsafeNativeMethods.WaitCommEvent(handle, eventsOccurredPtr, intOverlapped) == false)
                    {
                        int hr = Marshal.GetLastWin32Error(); 
                        if (hr == NativeMethods.ERROR_ACCESS_DENIED) {
                            doCleanup = true; 
                            break; 
                        }
                        if (hr == NativeMethods.ERROR_IO_PENDING) 
                        {
                            Debug.Assert(isAsync, "The port is not open for async, so we should not get ERROR_IO_PENDING from WaitCommEvent");
                            int error;
 
                            // if we get IO pending, MSDN says we should wait on the WaitHandle, then call GetOverlappedResult
                            // to get the results of WaitCommEvent. 
                            bool success = waitCommEventWaitHandle.WaitOne(); 
                            Debug.Assert(success, "waitCommEventWaitHandle.WaitOne() returned error " + Marshal.GetLastWin32Error());
 
                            do {
                                // NOTE: GetOverlappedResult will modify the original pointer passed into WaitCommEvent.
                                success = UnsafeNativeMethods.GetOverlappedResult(handle, intOverlapped, ref unused, false);
                                error = Marshal.GetLastWin32Error(); 
                            }
                            while (error == NativeMethods.ERROR_IO_INCOMPLETE && !ShutdownLoop && !success); 
 
                            if (!success) {
                                // Ignore ERROR_IO_INCOMPLETE and ERROR_INVALID_PARAMETER, because there's a chance we'll get 
                                // one of those while shutting down
                                if (! ( (error == NativeMethods.ERROR_IO_INCOMPLETE || error == NativeMethods.ERROR_INVALID_PARAMETER) && ShutdownLoop))
                                    Debug.Assert(false, "GetOverlappedResult returned error, we might leak intOverlapped memory" + error.ToString(CultureInfo.InvariantCulture));
                            } 
                        }
                        else if (hr != NativeMethods.ERROR_INVALID_PARAMETER) { 
                            // ignore ERROR_INVALID_PARAMETER errors.  WaitCommError seems to return this 
                            // when SetCommMask is changed while it's blocking (like we do in Dispose())
                            Debug.Assert(false, "WaitCommEvent returned error " + hr); 
                        }
                    }
                    }
 
                    if (!ShutdownLoop)
                        CallEvents(eventsOccurred); 
 
                    if (isAsync) {
                        if (Interlocked.Decrement(ref asyncResult._numBytes) == 0) 
                            Overlapped.Free(intOverlapped);
                    }
                }
                if (doCleanup) { 
                    // the rest will be handled in Dispose()
                    endEventLoop = true; 
                    Overlapped.Free(intOverlapped); 
                }
                eventLoopEndedSignal.Set(); 
            }

            private unsafe void FreeNativeOverlappedCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped) {
                // Unpack overlapped 
                Overlapped overlapped = Overlapped.Unpack(pOverlapped);
 
                // Extract the async result from overlapped structure 
                SerialStreamAsyncResult asyncResult =
                    (SerialStreamAsyncResult)overlapped.AsyncResult; 

                if (Interlocked.Decrement(ref asyncResult._numBytes) == 0)
                    Overlapped.Free(pOverlapped);
            } 

            private void CallEvents(int nativeEvents) 
            { 
                // first see if we had any error conditions
                if ((nativeEvents & NativeMethods.EV_ERR) != 0) { 
                    int errors = 0;
                    if (UnsafeNativeMethods.ClearCommError(handle, ref errors, IntPtr.Zero) == false) {
                        InternalResources.WinIOError();
                    } 

                    errors = errors & errorEvents; 
                    // 

 

                    if (errors != 0) {
                        ThreadPool.QueueUserWorkItem(callErrorEvents, errors);
                    } 
                }
 
                // now look for pin changed and received events. 
                if ((nativeEvents & pinChangedEvents) != 0) {
                    ThreadPool.QueueUserWorkItem(callPinEvents, nativeEvents); 
                }

                if ((nativeEvents & receivedEvents) != 0) {
                    ThreadPool.QueueUserWorkItem(callReceiveEvents, nativeEvents); 
                }
            } 
 

            private void CallErrorEvents(object state) { 
                int errors = (int) state;
                SerialStream stream = (SerialStream) streamWeakReference.Target;
                if (stream == null)
                    return; 

                if (stream.ErrorReceived != null) { 
                    if ((errors & (int) SerialError.TXFull) != 0) 
                        stream.ErrorReceived(stream, new SerialErrorReceivedEventArgs(SerialError.TXFull));
 
                    if ((errors & (int) SerialError.RXOver) != 0)
                        stream.ErrorReceived(stream, new SerialErrorReceivedEventArgs(SerialError.RXOver));

                    if ((errors & (int) SerialError.Overrun) != 0) 
                        stream.ErrorReceived(stream, new SerialErrorReceivedEventArgs(SerialError.Overrun));
 
                    if ((errors & (int) SerialError.RXParity) != 0) 
                        stream.ErrorReceived(stream, new SerialErrorReceivedEventArgs(SerialError.RXParity));
 
                    if ((errors & (int) SerialError.Frame) != 0)
                        stream.ErrorReceived(stream, new SerialErrorReceivedEventArgs(SerialError.Frame));
                }
 
                stream = null;
            } 
 
            private void CallReceiveEvents(object state) {
                int nativeEvents = (int) state; 
                SerialStream stream = (SerialStream) streamWeakReference.Target;
                if (stream == null)
                    return;
 
                if (stream.DataReceived != null) {
                    if ((nativeEvents & (int) SerialData.Chars) != 0) 
                        stream.DataReceived(stream, new SerialDataReceivedEventArgs(SerialData.Chars)); 
                    if ((nativeEvents & (int) SerialData.Eof) != 0)
                        stream.DataReceived(stream, new SerialDataReceivedEventArgs(SerialData.Eof)); 
                }

                stream = null;
            } 

            private void CallPinEvents(object state) { 
                int nativeEvents = (int) state; 

                SerialStream stream = (SerialStream) streamWeakReference.Target; 
                if (stream == null)
                    return;

                if (stream.PinChanged != null) { 
                    if ((nativeEvents & (int) SerialPinChange.CtsChanged) != 0)
                        stream.PinChanged(stream, new SerialPinChangedEventArgs(SerialPinChange.CtsChanged)); 
 
                    if ((nativeEvents & (int) SerialPinChange.DsrChanged) != 0)
                        stream.PinChanged(stream, new SerialPinChangedEventArgs(SerialPinChange.DsrChanged)); 

                    if ((nativeEvents & (int) SerialPinChange.CDChanged) != 0)
                        stream.PinChanged(stream, new SerialPinChangedEventArgs(SerialPinChange.CDChanged));
 
                    if ((nativeEvents & (int) SerialPinChange.Ring) != 0)
                        stream.PinChanged(stream, new SerialPinChangedEventArgs(SerialPinChange.Ring)); 
 
                    if ((nativeEvents & (int) SerialPinChange.Break) != 0)
                        stream.PinChanged(stream, new SerialPinChangedEventArgs(SerialPinChange.Break)); 
                }

                stream = null;
            } 

        } 
 

        // This is an internal object implementing IAsyncResult with fields 
        // for all of the relevant data necessary to complete the IO operation.
        // This is used by AsyncFSCallback and all async methods.
        unsafe internal sealed class SerialStreamAsyncResult : IAsyncResult
        { 
            // User code callback
            internal AsyncCallback _userCallback; 
 
            internal Object _userStateObject;
            internal GCHandle _bufferHandle;  // GCHandle to pin byte[]. 

            internal bool _isWrite;     // Whether this is a read or a write
            internal bool _isComplete;
            internal bool _completedSynchronously;  // Which thread called callback 
            internal bool _bufferIsPinned;   // Whether our _bufferHandle is valid.
 
            internal ManualResetEvent _waitHandle; 
            internal int _EndXxxCalled;   // Whether we've called EndXxx already.
            internal int _numBytes;     // number of bytes read OR written 
            internal int _errorCode;
            internal NativeOverlapped* _overlapped;

            public Object AsyncState 
            {
                get { return _userStateObject; } 
            } 

            public bool IsCompleted 
            {
                get { return _isComplete; }
            }
 
            public WaitHandle AsyncWaitHandle
            { 
                get { 
                    /*
                      // Consider uncommenting this someday soon - the EventHandle 
                      // in the Overlapped struct is really useless half of the
                      // time today since the OS doesn't signal it.  If users call
                      // EndXxx after the OS call happened to complete, there's no
                      // reason to create a synchronization primitive here.  Fixing 
                      // this will save us some perf, assuming we can correctly
                      // initialize the ManualResetEvent. 
                    if (_waitHandle == null) { 
                        ManualResetEvent mre = new ManualResetEvent(false);
                        if (_overlapped != null && _overlapped->EventHandle != IntPtr.Zero) 
                            mre.Handle = _overlapped->EventHandle;
                        if (_isComplete)
                            mre.Set();
                        _waitHandle = mre; 
                    }
                    */ 
                    return _waitHandle; 
                }
            } 

            // Returns true iff the user callback was called by the thread that
            // called BeginRead or BeginWrite.  If we use an async delegate or
            // threadpool thread internally, this will be false.  This is used 
            // by code to determine whether a successive call to BeginRead needs
            // to be done on their main thread or in their callback to avoid a 
            // stack overflow on many reads or writes. 
            public bool CompletedSynchronously
            { 
                get { return _completedSynchronously; }
            }

            // Methods to ensure that if we somehow return from Win32 ReadFile() or WriteFile() 
            // and fill the buffer at different times, the GC doesn't move said buffer.
            internal void PinBuffer(byte[] buffer) 
            { 
                _bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                _bufferIsPinned = true; 
            }

            internal void UnpinBuffer()
            { 
                if (_bufferIsPinned)
                { 
                    _bufferHandle.Free(); 
                    _bufferIsPinned = false;
                } 
            }
        }
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK