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