SerialStream.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Sys / System / IO / Ports / SerialStream.cs / 1305376 / 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.Collections;
using System.ComponentModel; 
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO; 
using System.Resources;
using System.Runtime; 
using System.Runtime.CompilerServices; 
using System.Runtime.InteropServices;
using System.Runtime.Remoting.Messaging; 
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text; 
using System.Threading;
 
using Microsoft.Win32; 
using Microsoft.Win32.SafeHandles;
 

// 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(); 

                    bool skipSPAccess = false; 
 
                    // turn off all events and signal WaitCommEvent
                    UnsafeNativeMethods.SetCommMask(_handle, 0); 
                    if(!UnsafeNativeMethods.EscapeCommFunction(_handle, NativeMethods.CLRDTR))
                    {
                        int hr = Marshal.GetLastWin32Error();
 
                        // access denied can happen if USB is yanked out. If that happens, we
                        // want to at least allow finalize to succeed and clean up everything 
                        // we can. To achieve this, we need to avoid further attempts to access 
                        // the SerialPort.
                        if (hr == NativeMethods.ERROR_ACCESS_DENIED && !disposing) { 
                            skipSPAccess = true;
                        }
                        else {
                            // should not happen 
                            InternalResources.WinIOError();
                        } 
                    } 

                    if (!skipSPAccess && !_handle.IsClosed) { 
                        Flush();
                    }

                    eventRunner.waitCommEventWaitHandle.Set(); 

                    if (!skipSPAccess) { 
                        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);

            // 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);

            // 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-------* 

        [ResourceExposure(ResourceScope.None)] 
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        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, array); 

            asyncResult._overlapped = intOverlapped; 
 
            // 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;
        } 

        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        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, array);
 
            asyncResult._overlapped = intOverlapped; 

            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 ignore 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 ignore 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. 
            [ResourceExposure(ResourceScope.None)] 
            [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
            [SuppressMessage("Microsoft.Interoperability", "CA1404:CallGetLastErrorImmediatelyAfterPInvoke", Justification = "this is debug-only code")] 
            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) 
            { 
                // EV_ERR includes only CE_FRAME, CE_OVERRUN, and CE_RXPARITY
                // To catch errors such as CE_RXOVER, we need to call CleanCommErrors bit more regularly. 
                // EV_RXCHAR is perhaps too loose an event to look for overflow errors but a safe side to err...
                if ((nativeEvents & (NativeMethods.EV_ERR | NativeMethods.EV_RXCHAR)) != 0) {
                    int errors = 0;
                    if (UnsafeNativeMethods.ClearCommError(handle, ref errors, IntPtr.Zero) == false) { 

                        //InternalResources.WinIOError(); 
 
                        // We don't want to throw an exception from the background thread which is un-catchable and hence tear down the process.
                        // At present we don't have a first class event that we can raise for this class of fatal errors. One possibility is 
                        // to overload SeralErrors event to include another enum (perhaps CE_IOE) that we can use for this purpose.
                        // In the absene of that, it is better to eat this error silently than tearing down the process (lesser of the evil).
                        // This uncleared comm error will most likely ---- up when the device is accessed by other APIs (such as Read) on the
                        // main thread and hence become known. It is bit roundabout but acceptable. 
                        //
                        // Shutdown the event runner loop (probably bit drastic but we did come across a fatal error). 
                        // Defer actual dispose chores until finalization though. 
                        endEventLoop = true;
                        Thread.MemoryBarrier(); 
                        return;
                    }

                    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 bool _isWrite;     // Whether this is a read or a write
            internal bool _isComplete; 
            internal bool _completedSynchronously;  // Which thread called callback

            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; } 
            } 
        }
    } 
}

// 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