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; } }
internal SerialStream(string portName, int baudRate, System.IO.Ports.Parity parity, int dataBits, System.IO.Ports.StopBits stopBits, int readTimeout, int writeTimeout, System.IO.Ports.Handshake handshake, bool dtrEnable, bool rtsEnable, bool discardNull, byte parityReplace) { int dwFlagsAndAttributes = 0x40000000; if (Environment.OSVersion.Platform == PlatformID.Win32Windows) { dwFlagsAndAttributes = 0x80; this.isAsync = false; } if ((portName == null) || !portName.StartsWith("COM", StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException(SR.GetString("Arg_InvalidSerialPort"), "portName"); } SafeFileHandle hFile = Microsoft.Win32.UnsafeNativeMethods.CreateFile(@"\\.\" + portName, -1073741824, 0, IntPtr.Zero, 3, dwFlagsAndAttributes, IntPtr.Zero); if (hFile.IsInvalid) { InternalResources.WinIOError(portName); } try { int fileType = Microsoft.Win32.UnsafeNativeMethods.GetFileType(hFile); if ((fileType != 2) && (fileType != 0)) { throw new ArgumentException(SR.GetString("Arg_InvalidSerialPort"), "portName"); } this._handle = hFile; this.portName = portName; this.handshake = handshake; this.parityReplace = parityReplace; this.tempBuf = new byte[1]; this.commProp = new Microsoft.Win32.UnsafeNativeMethods.COMMPROP(); int lpModemStat = 0; if (!Microsoft.Win32.UnsafeNativeMethods.GetCommProperties(this._handle, ref this.commProp) || !Microsoft.Win32.UnsafeNativeMethods.GetCommModemStatus(this._handle, ref lpModemStat)) { int errorCode = Marshal.GetLastWin32Error(); switch (errorCode) { case 0x57: case 6: throw new ArgumentException(SR.GetString("Arg_InvalidSerialPortExtended"), "portName"); } InternalResources.WinIOError(errorCode, string.Empty); } if ((this.commProp.dwMaxBaud != 0) && (baudRate > this.commProp.dwMaxBaud)) { throw new ArgumentOutOfRangeException("baudRate", SR.GetString("Max_Baud", new object[] { this.commProp.dwMaxBaud })); } this.comStat = new Microsoft.Win32.UnsafeNativeMethods.COMSTAT(); this.dcb = new Microsoft.Win32.UnsafeNativeMethods.DCB(); this.InitializeDCB(baudRate, parity, dataBits, stopBits, discardNull); this.DtrEnable = dtrEnable; this.rtsEnable = this.GetDcbFlag(12) == 1; if ((handshake != System.IO.Ports.Handshake.RequestToSend) && (handshake != System.IO.Ports.Handshake.RequestToSendXOnXOff)) { this.RtsEnable = rtsEnable; } if (readTimeout == 0) { this.commTimeouts.ReadTotalTimeoutConstant = 0; this.commTimeouts.ReadTotalTimeoutMultiplier = 0; this.commTimeouts.ReadIntervalTimeout = -1; } else if (readTimeout == -1) { this.commTimeouts.ReadTotalTimeoutConstant = -2; this.commTimeouts.ReadTotalTimeoutMultiplier = -1; this.commTimeouts.ReadIntervalTimeout = -1; } else { this.commTimeouts.ReadTotalTimeoutConstant = readTimeout; this.commTimeouts.ReadTotalTimeoutMultiplier = -1; this.commTimeouts.ReadIntervalTimeout = -1; } this.commTimeouts.WriteTotalTimeoutMultiplier = 0; this.commTimeouts.WriteTotalTimeoutConstant = (writeTimeout == -1) ? 0 : writeTimeout; if (!Microsoft.Win32.UnsafeNativeMethods.SetCommTimeouts(this._handle, ref this.commTimeouts)) { InternalResources.WinIOError(); } if (this.isAsync && !ThreadPool.BindHandle(this._handle)) { throw new IOException(SR.GetString("IO_BindHandleFailed")); } Microsoft.Win32.UnsafeNativeMethods.SetCommMask(this._handle, 0x1fb); this.eventRunner = new EventLoopRunner(this); new Thread(new ThreadStart(this.eventRunner.WaitForCommEvent)) { IsBackground = true }.Start(); } catch { hFile.Close(); this._handle = null; throw; } }