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;
            }
        }
Example #2
0
        private HandleProtector _handleProtector; // See the HandleProtector class.

        #endregion Fields

        #region Constructors

        // -----------SECTION: constructor --------------------------*
        // this method is used by SerialPort upon SerialStream's creation
        internal SerialStream(string resource, int baudRate, Parity parity, int dataBits, StopBits stopBits, int readTimeout, int writeTimeout, Handshake handshake,
            bool dtrEnable, bool rtsEnable, bool discardNull, byte parityReplace)
        {
            //Error checking done in SerialPort.

            IntPtr tempHandle = UnsafeNativeMethods.CreateFile("\\\\.\\" + resource,
                NativeMethods.GENERIC_READ | NativeMethods.GENERIC_WRITE,
                0,    // comm devices must be opened w/exclusive-access
                NativeMethods.NULL, // no security attributes
                UnsafeNativeMethods.OPEN_EXISTING, // comm devices must use OPEN_EXISTING
                UnsafeNativeMethods.FILE_FLAG_OVERLAPPED |
                UnsafeNativeMethods.FILE_ATTRIBUTE_NORMAL,    // async I/O
                NativeMethods.NULL  // hTemplate must be NULL for comm devices
                );

            if (tempHandle == NativeMethods.INVALID_HANDLE_VALUE)
            {
                int errorCode = Marshal.GetLastWin32Error();
                switch (errorCode)
                {
                    case NativeMethods.ERROR_FILE_NOT_FOUND:
                        throw new FileNotFoundException("ERROR_FILE_NOT_FOUND", InternalResources.GetResourceString("IO.FileNotFound_FileName", resource));

                    case NativeMethods.ERROR_ACCESS_DENIED:
                        throw new UnauthorizedAccessException(InternalResources.GetResourceString("UnauthorizedAccess_IODenied_Path", resource));
                    default:
                        InternalResources.WinIOError();
                        break;
                }
            }

            if (UnsafeNativeMethods.GetFileType(tempHandle) != UnsafeNativeMethods.FILE_TYPE_CHAR)
                throw new ArgumentException("INVALID_RESOURCE_FILE", InternalResources.GetResourceString("Arg_InvalidResourceFile"));

            _handleProtector = new __HandleProtector(tempHandle, true);

            // set properties of the stream that exist as members in SerialStream
            this.portName = resource;
            this.handshake = handshake;
            this.dtrEnable = dtrEnable;
            this.rtsEnable = rtsEnable;
            this.parityReplace = parityReplace;

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

            // fill COMMPROPERTIES struct, which has our maximum allowed baud rate
            commProp = new UnsafeNativeMethods.COMMPROP();
            if (UnsafeNativeMethods.GetCommProperties(_handleProtector.Handle, ref commProp) == false)
            {
                UnsafeNativeMethods.CloseHandle(_handleProtector.Handle);
                InternalResources.WinIOError();
            }
            if (baudRate > commProp.dwMaxBaud)
                throw new ArgumentOutOfRangeException("baudRate", "Requested baud greater than maximum for this device driver = " + 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);

            // set timeout defaults
            commTimeouts.ReadIntervalTimeout = (readTimeout == SerialPort.InfiniteTimeout) ? 0 : NativeMethods.MAXDWORD;
            commTimeouts.ReadTotalTimeoutMultiplier = (readTimeout > 0 && readTimeout != SerialPort.InfiniteTimeout)
                                                            ? NativeMethods.MAXDWORD : 0;
            commTimeouts.ReadTotalTimeoutConstant = (readTimeout > 0 && readTimeout != SerialPort.InfiniteTimeout) ?
                                                            readTimeout : 0;
            commTimeouts.WriteTotalTimeoutMultiplier = 0;
            commTimeouts.WriteTotalTimeoutConstant = ((writeTimeout == SerialPort.InfiniteTimeout) ?
                                                            0 : writeTimeout);
            // note - we cannot have a meaningful conception of a 0 write timeout, since every write takes at
            // least 0 mills.

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

            if (!ThreadPool.BindHandle(_handleProtector.Handle))
            {
                throw new IOException(InternalResources.GetResourceString("IO.IO_BindHandleFailed"));
            }

            // prep. for starting event cycle.
            myWaitCommCallback = new WaitEventCallback(WaitForCommEvent);
            myAsyncCallback = new AsyncCallback(EndWaitForCommEvent);
            state = null;    // no need for new object, since we never use it.

            IAsyncResult ar = myWaitCommCallback.BeginInvoke(myAsyncCallback, state);
        }