1 // ==++== 2 // 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // 5 // ==--== 6 /*============================================================ 7 ** 8 ** Class: SerialPort 9 ** 10 ** Purpose: SerialPort wraps an internal SerialStream class, 11 ** : providing a high but complete level of Serial Port I/O functionality 12 ** : over the handle/Win32 object level of the SerialStream. 13 ** 14 ** 15 ** Date: August 2002 16 ** 17 ===========================================================*/ 18 19 20 using System; 21 using System.ComponentModel; 22 using System.Collections; 23 using System.Diagnostics; 24 using System.IO; 25 using System.Text; 26 using System.Security; 27 using System.Security.Permissions; 28 using Microsoft.Win32; 29 using System.Runtime.InteropServices; 30 using System.Runtime.Versioning; 31 32 33 namespace System.IO.Ports 34 { 35 36 [MonitoringDescription(SR.SerialPortDesc)] 37 public class SerialPort : System.ComponentModel.Component 38 { 39 public const int InfiniteTimeout = -1; 40 41 // ---------- default values -------------* 42 43 private const int defaultDataBits = 8; 44 private const Parity defaultParity = Parity.None; 45 private const StopBits defaultStopBits = StopBits.One; 46 private const Handshake defaultHandshake = Handshake.None; 47 private const int defaultBufferSize = 1024; 48 private const string defaultPortName = "COM1"; 49 private const int defaultBaudRate = 9600; 50 private const bool defaultDtrEnable = false; 51 private const bool defaultRtsEnable = false; 52 private const bool defaultDiscardNull = false; 53 private const byte defaultParityReplace = (byte) '?'; 54 private const int defaultReceivedBytesThreshold = 1; 55 private const int defaultReadTimeout = SerialPort.InfiniteTimeout; 56 private const int defaultWriteTimeout = SerialPort.InfiniteTimeout; 57 private const int defaultReadBufferSize = 4096; 58 private const int defaultWriteBufferSize = 2048; 59 private const int maxDataBits = 8; 60 private const int minDataBits = 5; 61 private const string defaultNewLine = "\n"; 62 63 private const string SERIAL_NAME = @"\Device\Serial"; 64 65 // --------- members supporting exposed properties ------------* 66 private int baudRate = defaultBaudRate; 67 private int dataBits = defaultDataBits; 68 private Parity parity = defaultParity; 69 private StopBits stopBits = defaultStopBits; 70 private string portName = defaultPortName; 71 private Encoding encoding = System.Text.Encoding.ASCII; // ASCII is default encoding for modem communication, etc. 72 private Decoder decoder = System.Text.Encoding.ASCII.GetDecoder(); 73 private int maxByteCountForSingleChar = System.Text.Encoding.ASCII.GetMaxByteCount(1); 74 private Handshake handshake = defaultHandshake; 75 private int readTimeout = defaultReadTimeout; 76 private int writeTimeout = defaultWriteTimeout; 77 private int receivedBytesThreshold = defaultReceivedBytesThreshold; 78 private bool discardNull = defaultDiscardNull; 79 private bool dtrEnable = defaultDtrEnable; 80 private bool rtsEnable = defaultRtsEnable; 81 private byte parityReplace = defaultParityReplace; 82 private string newLine = defaultNewLine; 83 private int readBufferSize = defaultReadBufferSize; 84 private int writeBufferSize = defaultWriteBufferSize; 85 86 // ---------- members for internal support ---------* 87 private SerialStream internalSerialStream = null; 88 private byte[] inBuffer = new byte[defaultBufferSize]; 89 private int readPos = 0; // position of next byte to read in the read buffer. readPos <= readLen 90 private int readLen = 0; // position of first unreadable byte => CachedBytesToRead is the number of readable bytes left. 91 private char[] oneChar = new char[1]; 92 private char[] singleCharBuffer = null; 93 94 // ------ event members ------------------* 95 //public event EventHandler Disposed; 96 [MonitoringDescription(SR.SerialErrorReceived)] 97 public event SerialErrorReceivedEventHandler ErrorReceived; 98 99 [MonitoringDescription(SR.SerialPinChanged)] 100 public event SerialPinChangedEventHandler PinChanged; 101 102 [MonitoringDescription(SR.SerialDataReceived)] 103 public event SerialDataReceivedEventHandler DataReceived; 104 105 //--- component properties---------------* 106 107 // ---- SECTION: public properties --------------* 108 // Note: information about port properties passes in ONE direction: from SerialPort to 109 // its underlying Stream. No changes are able to be made in the important properties of 110 // the stream and its behavior, so no reflection back to SerialPort is necessary. 111 112 // Gets the internal SerialStream object. Used to pass essence of SerialPort to another Stream wrapper. 113 [Browsable(false)] 114 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 115 public Stream BaseStream 116 { 117 get { 118 if (!IsOpen) 119 throw new InvalidOperationException(SR.GetString(SR.BaseStream_Invalid_Not_Open)); 120 121 return internalSerialStream; 122 } 123 } 124 125 [Browsable(true), 126 DefaultValue(defaultBaudRate), 127 MonitoringDescription(SR.BaudRate)] 128 public int BaudRate 129 { 130 get { return baudRate; } 131 set { 132 if (value <= 0) 133 throw new ArgumentOutOfRangeException("BaudRate", SR.GetString(SR.ArgumentOutOfRange_NeedPosNum)); 134 135 if (IsOpen) 136 internalSerialStream.BaudRate = value; 137 baudRate = value; 138 } 139 } 140 141 [Browsable(false)] 142 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 143 public bool BreakState 144 { 145 get 146 { 147 if (!IsOpen) 148 throw new InvalidOperationException(SR.GetString(SR.Port_not_open)); 149 150 return internalSerialStream.BreakState; 151 } 152 153 set { 154 if (!IsOpen) 155 throw new InvalidOperationException(SR.GetString(SR.Port_not_open)); 156 157 internalSerialStream.BreakState = value; 158 } 159 } 160 161 // includes all bytes available on serial driver's output buffer. Note that we do not internally buffer output bytes in SerialPort. 162 [Browsable(false)] 163 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 164 public int BytesToWrite 165 { 166 get 167 { 168 if (!IsOpen) 169 throw new InvalidOperationException(SR.GetString(SR.Port_not_open)); 170 return internalSerialStream.BytesToWrite; 171 } 172 } 173 174 // includes all bytes available on serial driver's input buffer as well as bytes internally buffered int the SerialPort class. 175 [Browsable(false)] 176 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 177 public int BytesToRead 178 { 179 get 180 { 181 if (!IsOpen) 182 throw new InvalidOperationException(SR.GetString(SR.Port_not_open)); 183 return internalSerialStream.BytesToRead + CachedBytesToRead; // count the number of bytes we have in the internal buffer too. 184 } 185 } 186 187 private int CachedBytesToRead { 188 get { 189 return readLen - readPos; 190 } 191 } 192 193 [Browsable(false)] 194 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 195 public bool CDHolding 196 { 197 get 198 { 199 if (!IsOpen) 200 throw new InvalidOperationException(SR.GetString(SR.Port_not_open)); 201 return internalSerialStream.CDHolding; 202 } 203 } 204 205 [Browsable(false)] 206 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 207 public bool CtsHolding 208 { 209 get 210 { 211 if (!IsOpen) 212 throw new InvalidOperationException(SR.GetString(SR.Port_not_open)); 213 return internalSerialStream.CtsHolding; 214 } 215 } 216 217 218 [Browsable(true), 219 DefaultValue(defaultDataBits), 220 MonitoringDescription(SR.DataBits)] 221 public int DataBits 222 { 223 get 224 { return dataBits; } 225 set 226 { 227 if (value < minDataBits || value > maxDataBits) 228 throw new ArgumentOutOfRangeException("DataBits", SR.GetString(SR.ArgumentOutOfRange_Bounds_Lower_Upper, minDataBits, maxDataBits)); 229 230 if (IsOpen) 231 internalSerialStream.DataBits = value; 232 dataBits = value; 233 } 234 } 235 236 [Browsable(true), 237 DefaultValue(defaultDiscardNull), 238 MonitoringDescription(SR.DiscardNull)] 239 public bool DiscardNull 240 { 241 get 242 { 243 return discardNull; 244 } 245 set 246 { 247 if (IsOpen) 248 internalSerialStream.DiscardNull = value; 249 discardNull = value; 250 } 251 } 252 253 [Browsable(false)] 254 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 255 public bool DsrHolding 256 { 257 get 258 { 259 if (!IsOpen) 260 throw new InvalidOperationException(SR.GetString(SR.Port_not_open)); 261 return internalSerialStream.DsrHolding; 262 } 263 } 264 265 [Browsable(true), 266 DefaultValue(defaultDtrEnable), 267 MonitoringDescription(SR.DtrEnable)] 268 public bool DtrEnable 269 { 270 get { 271 if (IsOpen) 272 dtrEnable = internalSerialStream.DtrEnable; 273 274 return dtrEnable; 275 } 276 set 277 { 278 if (IsOpen) 279 internalSerialStream.DtrEnable = value; 280 dtrEnable = value; 281 } 282 } 283 284 // Allows specification of an arbitrary encoding for the reading and writing functions of the port 285 // which deal with chars and strings. Set by default in the code to System.Text.ASCIIEncoding(), which 286 // is the standard text encoding for modem commands and most of serial communication. 287 // Clearly not designable. 288 [Browsable(false), 289 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), 290 MonitoringDescription(SR.EncodingMonitoringDescription)] 291 public Encoding Encoding 292 { 293 get { 294 return encoding; 295 } 296 set { 297 if (value == null) 298 throw new ArgumentNullException("Encoding"); 299 300 // Limit the encodings we support to some known ones. The code pages < 50000 represent all of the single-byte 301 // and double-byte code pages. Code page 54936 is GB18030. Finally we check that the encoding's assembly 302 // is mscorlib, so we don't get any weird user encodings that happen to set a code page less than 50000. 303 if (!(value is ASCIIEncoding || value is UTF8Encoding || value is UnicodeEncoding || value is UTF32Encoding || 304 ((value.CodePage < 50000 || value.CodePage == 54936)&& value.GetType().Assembly == typeof(String).Assembly))) { 305 306 throw new ArgumentException(SR.GetString(SR.NotSupportedEncoding, value.WebName), "value"); 307 } 308 309 encoding = value; 310 decoder = encoding.GetDecoder(); 311 312 // This is somewhat of an approximate guesstimate to get the max char[] size needed to encode a single character 313 maxByteCountForSingleChar = encoding.GetMaxByteCount(1); 314 singleCharBuffer = null; 315 } 316 } 317 318 [Browsable(true), 319 DefaultValue(defaultHandshake), 320 MonitoringDescription(SR.Handshake)] 321 public Handshake Handshake 322 { 323 get 324 { 325 return handshake; 326 } 327 set 328 { 329 if (value < Handshake.None || value > Handshake.RequestToSendXOnXOff) 330 throw new ArgumentOutOfRangeException("Handshake", SR.GetString(SR.ArgumentOutOfRange_Enum)); 331 332 if (IsOpen) 333 internalSerialStream.Handshake = value; 334 handshake = value; 335 } 336 } 337 338 // true only if the Open() method successfully called on this SerialPort object, without Close() being called more recently. 339 [Browsable(false)] 340 public bool IsOpen 341 { 342 get { return (internalSerialStream != null && internalSerialStream.IsOpen); } 343 } 344 345 [ 346 Browsable(false), 347 DefaultValue(defaultNewLine), 348 MonitoringDescription(SR.NewLine) 349 ] 350 public string NewLine { 351 get { return newLine; } 352 set { 353 if (value == null) 354 throw new ArgumentNullException(); 355 if (value.Length == 0) 356 throw new ArgumentException(SR.GetString(SR.InvalidNullEmptyArgument, "NewLine")); 357 358 newLine = value; 359 } 360 } 361 362 [Browsable(true), 363 DefaultValue(defaultParity), 364 MonitoringDescription(SR.Parity)] 365 public Parity Parity 366 { 367 get 368 { 369 370 return parity; 371 } 372 set 373 { 374 if (value < Parity.None || value > Parity.Space) 375 throw new ArgumentOutOfRangeException("Parity", SR.GetString(SR.ArgumentOutOfRange_Enum)); 376 377 if (IsOpen) 378 internalSerialStream.Parity = value; 379 parity = value; 380 } 381 } 382 383 [Browsable(true), 384 DefaultValue(defaultParityReplace), 385 MonitoringDescription(SR.ParityReplace)] 386 public byte ParityReplace 387 { 388 get { return parityReplace; } 389 set 390 { 391 if (IsOpen) 392 internalSerialStream.ParityReplace = value; 393 parityReplace = value; 394 } 395 } 396 397 398 399 // Note that the communications port cannot be meaningfully re-set when the port is open, 400 // and so once set by the constructor becomes read-only. 401 [Browsable(true), 402 DefaultValue(defaultPortName), 403 MonitoringDescription(SR.PortName)] 404 public string PortName 405 { 406 get { 407 return portName; 408 } 409 [ResourceExposure(ResourceScope.Machine)] 410 set 411 { 412 if (value == null) 413 throw new ArgumentNullException("PortName"); 414 if (value.Length ==0) 415 throw new ArgumentException(SR.GetString(SR.PortNameEmpty_String), "PortName"); 416 417 // disallow access to device resources beginning with @"\\", instead requiring "COM2:", etc. 418 // Note that this still allows freedom in mapping names to ports, etc., but blocks security leaks. 419 if (value.StartsWith("\\\\", StringComparison.Ordinal)) 420 throw new ArgumentException(SR.GetString(SR.Arg_SecurityException), "PortName"); 421 422 if (IsOpen) 423 throw new InvalidOperationException(SR.GetString(SR.Cant_be_set_when_open, "PortName")); 424 portName = value; 425 } 426 } 427 428 [Browsable(true), 429 DefaultValue(defaultReadBufferSize), 430 MonitoringDescription(SR.ReadBufferSize)] 431 public int ReadBufferSize { 432 get { 433 return readBufferSize; 434 } 435 set { 436 if (value <= 0) 437 throw new ArgumentOutOfRangeException("value"); 438 439 if (IsOpen) 440 throw new InvalidOperationException(SR.GetString(SR.Cant_be_set_when_open, "value")); 441 442 readBufferSize = value; 443 } 444 } 445 446 // timeout for all read operations. May be set to SerialPort.InfiniteTimeout, 0, or any positive value 447 [Browsable(true), 448 DefaultValue(SerialPort.InfiniteTimeout), 449 MonitoringDescription(SR.ReadTimeout)] 450 public int ReadTimeout 451 { 452 get 453 { 454 return readTimeout; 455 } 456 457 set 458 { 459 if (value < 0 && value != SerialPort.InfiniteTimeout) 460 throw new ArgumentOutOfRangeException("ReadTimeout", SR.GetString(SR.ArgumentOutOfRange_Timeout)); 461 462 if (IsOpen) 463 internalSerialStream.ReadTimeout = value; 464 readTimeout = value; 465 } 466 } 467 468 [Browsable(true), 469 DefaultValue(defaultReceivedBytesThreshold), 470 MonitoringDescription(SR.ReceivedBytesThreshold)] 471 // If we have the SerialData.Chars event set, this property indicates the number of bytes necessary 472 // to exist in our buffers before the event is thrown. This is useful if we expect to receive n-byte 473 // packets and can only act when we have this many, etc. 474 public int ReceivedBytesThreshold 475 { 476 get 477 { 478 return receivedBytesThreshold; 479 } 480 481 set 482 { 483 if (value <= 0) 484 throw new ArgumentOutOfRangeException("ReceivedBytesThreshold", 485 SR.GetString(SR.ArgumentOutOfRange_NeedPosNum)); 486 receivedBytesThreshold = value; 487 488 if (IsOpen) { 489 // fake the call to our event handler in case the threshold has been set lower 490 // than how many bytes we currently have. 491 SerialDataReceivedEventArgs args = new SerialDataReceivedEventArgs(SerialData.Chars); 492 CatchReceivedEvents(this, args); 493 } 494 } 495 } 496 497 [Browsable(true), 498 DefaultValue(defaultRtsEnable), 499 MonitoringDescription(SR.RtsEnable)] 500 public bool RtsEnable 501 { 502 get 503 { 504 if (IsOpen) 505 rtsEnable = internalSerialStream.RtsEnable; 506 507 return rtsEnable; 508 } 509 set 510 { 511 if (IsOpen) 512 internalSerialStream.RtsEnable = value; 513 rtsEnable = value; 514 } 515 } 516 517 // StopBits represented in C# as StopBits enum type and in Win32 as an integer 1, 2, or 3. 518 [Browsable(true), 519 DefaultValue(defaultStopBits), 520 MonitoringDescription(SR.StopBits) 521 ] 522 public StopBits StopBits 523 { 524 get 525 { 526 return stopBits; 527 } 528 set 529 { 530 // this range check looks wrong, but it really is correct. One = 1, Two = 2, and OnePointFive = 3 531 if (value < StopBits.One || value > StopBits.OnePointFive) 532 throw new ArgumentOutOfRangeException("StopBits", SR.GetString(SR.ArgumentOutOfRange_Enum)); 533 534 if (IsOpen) 535 internalSerialStream.StopBits = value; 536 stopBits = value; 537 } 538 } 539 540 [Browsable(true), 541 DefaultValue(defaultWriteBufferSize), 542 MonitoringDescription(SR.WriteBufferSize)] 543 public int WriteBufferSize { 544 get { 545 return writeBufferSize; 546 } 547 set { 548 if (value <= 0) 549 throw new ArgumentOutOfRangeException("value"); 550 551 if (IsOpen) 552 throw new InvalidOperationException(SR.GetString(SR.Cant_be_set_when_open, "value")); 553 554 writeBufferSize = value; 555 } 556 } 557 558 // timeout for all write operations. May be set to SerialPort.InfiniteTimeout or any positive value 559 [Browsable(true), 560 DefaultValue(defaultWriteTimeout), 561 MonitoringDescription(SR.WriteTimeout)] 562 public int WriteTimeout 563 { 564 get 565 { 566 return writeTimeout; 567 } 568 set 569 { 570 if (value <= 0 && value != SerialPort.InfiniteTimeout) 571 throw new ArgumentOutOfRangeException("WriteTimeout", SR.GetString(SR.ArgumentOutOfRange_WriteTimeout)); 572 573 if (IsOpen) 574 internalSerialStream.WriteTimeout = value; 575 writeTimeout = value; 576 } 577 } 578 579 580 581 // -------- SECTION: constructors -----------------* SerialPort(System.ComponentModel.IContainer container)582 public SerialPort(System.ComponentModel.IContainer container) 583 { 584 /// 585 /// Required for Windows.Forms Class Composition Designer support 586 /// 587 container.Add(this); 588 } 589 SerialPort()590 public SerialPort() 591 { 592 } 593 594 // Non-design SerialPort constructors here chain, using default values for members left unspecified by parameters 595 // Note: Calling SerialPort() does not open a port connection but merely instantiates an object. 596 // : A connection must be made using SerialPort's Open() method. 597 [ResourceExposure(ResourceScope.Machine)] 598 [ResourceConsumption(ResourceScope.Machine)] SerialPort(string portName)599 public SerialPort(string portName) : this (portName, defaultBaudRate, defaultParity, defaultDataBits, defaultStopBits) 600 { 601 } 602 603 [ResourceExposure(ResourceScope.Machine)] 604 [ResourceConsumption(ResourceScope.Machine)] SerialPort(string portName, int baudRate)605 public SerialPort(string portName, int baudRate) : this (portName, baudRate, defaultParity, defaultDataBits, defaultStopBits) 606 { 607 } 608 609 [ResourceExposure(ResourceScope.Machine)] 610 [ResourceConsumption(ResourceScope.Machine)] SerialPort(string portName, int baudRate, Parity parity)611 public SerialPort(string portName, int baudRate, Parity parity) : this (portName, baudRate, parity, defaultDataBits, defaultStopBits) 612 { 613 } 614 615 [ResourceExposure(ResourceScope.Machine)] 616 [ResourceConsumption(ResourceScope.Machine)] SerialPort(string portName, int baudRate, Parity parity, int dataBits)617 public SerialPort(string portName, int baudRate, Parity parity, int dataBits) : this (portName, baudRate, parity, dataBits, defaultStopBits) 618 { 619 } 620 621 // all the magic happens in the call to the instance's .Open() method. 622 // Internally, the SerialStream constructor opens the file handle, sets the device 623 // control block and associated Win32 structures, and begins the event-watching cycle. 624 [ResourceExposure(ResourceScope.Machine)] 625 [ResourceConsumption(ResourceScope.Machine)] SerialPort(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits)626 public SerialPort(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits) 627 { 628 this.PortName = portName; 629 this.BaudRate = baudRate; 630 this.Parity = parity; 631 this.DataBits = dataBits; 632 this.StopBits = stopBits; 633 } 634 635 // Calls internal Serial Stream's Close() method on the internal Serial Stream. Close()636 public void Close() 637 { 638 Dispose(); 639 } 640 Dispose( bool disposing )641 protected override void Dispose( bool disposing ) 642 { 643 if( disposing ) { 644 if (IsOpen) { 645 internalSerialStream.Flush(); 646 internalSerialStream.Close(); 647 internalSerialStream = null; 648 } 649 } 650 base.Dispose( disposing ); 651 } 652 DiscardInBuffer()653 public void DiscardInBuffer() 654 { 655 if (!IsOpen) 656 throw new InvalidOperationException(SR.GetString(SR.Port_not_open)); 657 internalSerialStream.DiscardInBuffer(); 658 readPos = readLen = 0; 659 } 660 DiscardOutBuffer()661 public void DiscardOutBuffer() 662 { 663 if (!IsOpen) 664 throw new InvalidOperationException(SR.GetString(SR.Port_not_open)); 665 internalSerialStream.DiscardOutBuffer(); 666 } 667 668 [ResourceExposure(ResourceScope.Machine)] 669 [ResourceConsumption(ResourceScope.Machine)] GetPortNames()670 public static string[] GetPortNames() { 671 RegistryKey baseKey = null; 672 RegistryKey serialKey = null; 673 674 String[] portNames = null; 675 676 RegistryPermission registryPermission = new RegistryPermission(RegistryPermissionAccess.Read, 677 @"HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM"); 678 registryPermission.Assert(); 679 680 try { 681 baseKey = Registry.LocalMachine; 682 serialKey = baseKey.OpenSubKey(@"HARDWARE\DEVICEMAP\SERIALCOMM", false); 683 684 if (serialKey != null) { 685 686 string[] deviceNames = serialKey.GetValueNames(); 687 portNames = new String[deviceNames.Length]; 688 689 for (int i=0; i<deviceNames.Length; i++) 690 portNames[i] = (string)serialKey.GetValue(deviceNames[i]); 691 } 692 } 693 finally { 694 if (baseKey != null) 695 baseKey.Close(); 696 697 if (serialKey != null) 698 serialKey.Close(); 699 700 RegistryPermission.RevertAssert(); 701 } 702 703 // If serialKey didn't exist for some reason 704 if (portNames == null) 705 portNames = new String[0]; 706 707 return portNames; 708 } 709 710 #if NYT GetPortNames()711 public static string[] GetPortNames() { 712 if (Environment.OSVersion.Platform == PlatformID.Win32Windows) 713 throw new PlatformNotSupportedException(SR.GetString(SR.NotSupportedOS)); 714 715 // Get all the registered serial device names 716 RegistryPermission registryPermission = new RegistryPermission(PermissionState.Unrestricted); 717 registryPermission.Assert(); 718 719 RegistryKey baseKey = null; 720 RegistryKey serialKey = null; 721 722 Hashtable portNames = new Hashtable(10); 723 724 try { 725 baseKey = Registry.LocalMachine; 726 serialKey = baseKey.OpenSubKey(@"HARDWARE\DEVICEMAP\SERIALCOMM", true); 727 728 if (serialKey != null) { 729 730 string[] devices = serialKey.GetValueNames(); 731 for (int j=0; j<devices.Length; j++) { 732 portNames.Add(devices[j], null); 733 } 734 } 735 } 736 finally { 737 if (baseKey != null) 738 baseKey.Close(); 739 740 if (serialKey != null) 741 serialKey.Close(); 742 743 RegistryPermission.RevertAssert(); 744 } 745 746 // Get all the MS-DOS names on the local machine 747 //(sending null for lpctstrName gets all the names) 748 int dataSize; 749 char[] buffer = CallQueryDosDevice(null, out dataSize); 750 751 // From QueryDosDevice, we get back a long string where the names are delimited by \0 and the end 752 // of the string is indicated by two \0s 753 ArrayList names = new ArrayList(); 754 ArrayList deviceNames = new ArrayList(); 755 756 int i=0; 757 while (i < dataSize) { 758 // Walk through the buffer building a name until we hit the delimiter \0 759 int start = i; 760 while (buffer[i] != '\0') { 761 i++; 762 } 763 764 if (i != start) { 765 // We now have an MS-DOS name (the common name). We call QueryDosDevice again with 766 // this name to get the underlying system name mapped to the MS-DOS name. 767 string currentName = (new String(buffer, start, i-start)).Trim(); 768 int nameSize; 769 char[] nameBuffer = CallQueryDosDevice(currentName, out nameSize); 770 771 // If we got a system name, see if it's a serial port name. If it is, add the common name 772 // to our list 773 if (nameSize > 0) { 774 // internalName will include the trailing null chars as well as any additional 775 // names that may get returned. This is ok, since we are only interested in the 776 // first name and we can use StartsWith. 777 string internalName = new string(nameBuffer, 0, nameSize-2).Trim(); 778 779 if (internalName.StartsWith(SERIAL_NAME) || portNames.ContainsKey(internalName)) { 780 names.Add(currentName); 781 deviceNames.Add(internalName); 782 } 783 } 784 } 785 i++; 786 } 787 788 string[] namesArray = new String[names.Count]; 789 names.CopyTo(namesArray); 790 791 string[] deviceNamesArray = new String[deviceNames.Count]; 792 deviceNames.CopyTo(deviceNamesArray); 793 794 // sort the common names according to their actual device ordering 795 Array.Sort(deviceNamesArray, namesArray, Comparer.DefaultInvariant); 796 797 return namesArray; 798 } 799 CallQueryDosDevice(string name, out int dataSize)800 private static unsafe char[] CallQueryDosDevice(string name, out int dataSize) { 801 char[] buffer = new char[1024]; 802 803 fixed (char *bufferPtr = buffer) { 804 dataSize = UnsafeNativeMethods.QueryDosDevice(name, buffer, buffer.Length); 805 while (dataSize <= 0) { 806 int lastError = Marshal.GetLastWin32Error(); 807 if (lastError == NativeMethods.ERROR_INSUFFICIENT_BUFFER || lastError == NativeMethods.ERROR_MORE_DATA) { 808 buffer = new char[buffer.Length * 2]; 809 dataSize = UnsafeNativeMethods.QueryDosDevice(null, buffer, buffer.Length); 810 } 811 else { 812 throw new Win32Exception(); 813 } 814 } 815 } 816 return buffer; 817 } 818 #endif 819 820 // SerialPort is open <=> SerialPort has an associated SerialStream. 821 // The two statements are functionally equivalent here, so this method basically calls underlying Stream's 822 // constructor from the main properties specified in SerialPort: baud, stopBits, parity, dataBits, 823 // comm portName, handshaking, and timeouts. 824 [ResourceExposure(ResourceScope.None)] // Look at Name property 825 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] Open()826 public void Open() 827 { 828 if (IsOpen) 829 throw new InvalidOperationException(SR.GetString(SR.Port_already_open)); 830 831 // Demand unmanaged code permission 832 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); 833 834 internalSerialStream = new SerialStream(portName, baudRate, parity, dataBits, stopBits, readTimeout, 835 writeTimeout, handshake, dtrEnable, rtsEnable, discardNull, parityReplace); 836 837 internalSerialStream.SetBufferSizes(readBufferSize, writeBufferSize); 838 839 internalSerialStream.ErrorReceived += new SerialErrorReceivedEventHandler(CatchErrorEvents); 840 internalSerialStream.PinChanged += new SerialPinChangedEventHandler(CatchPinChangedEvents); 841 internalSerialStream.DataReceived += new SerialDataReceivedEventHandler(CatchReceivedEvents); 842 } 843 844 // Read Design pattern: 845 // : ReadChar() returns the first available full char if found before, throws TimeoutExc if timeout. 846 // : Read(byte[] buffer..., int count) returns all data available before read timeout expires up to *count* bytes 847 // : Read(char[] buffer..., int count) returns all data available before read timeout expires up to *count* chars. 848 // : Note, this does not return "half-characters". 849 // : ReadByte() is the binary analogue of the first one. 850 // : ReadLine(): returns null string on timeout, saves received data in buffer 851 // : ReadAvailable(): returns all full characters which are IMMEDIATELY available. 852 Read(byte[] buffer, int offset, int count)853 public int Read(byte[] buffer, int offset, int count) 854 { 855 if (!IsOpen) 856 throw new InvalidOperationException(SR.GetString(SR.Port_not_open)); 857 if (buffer==null) 858 throw new ArgumentNullException("buffer", SR.GetString(SR.ArgumentNull_Buffer)); 859 if (offset < 0) 860 throw new ArgumentOutOfRangeException("offset", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired)); 861 if (count < 0) 862 throw new ArgumentOutOfRangeException("count", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired)); 863 if (buffer.Length - offset < count) 864 throw new ArgumentException(SR.GetString(SR.Argument_InvalidOffLen)); 865 int bytesReadToBuffer=0; 866 867 // if any bytes available in internal buffer, return those without calling any read ops. 868 if (CachedBytesToRead >= 1) 869 { 870 bytesReadToBuffer = Math.Min(CachedBytesToRead, count); 871 Buffer.BlockCopy(inBuffer, readPos, buffer, offset, bytesReadToBuffer); 872 readPos += bytesReadToBuffer; 873 if (bytesReadToBuffer == count) { 874 if (readPos == readLen) readPos = readLen = 0; // just a check to see if we can reset buffer 875 return count; 876 } 877 878 // if we have read some bytes but there's none immediately available, return. 879 if (BytesToRead == 0) 880 return bytesReadToBuffer; 881 } 882 883 Debug.Assert(CachedBytesToRead == 0, "there should be nothing left in our internal buffer"); 884 readLen = readPos = 0; 885 886 int bytesLeftToRead = count - bytesReadToBuffer; 887 888 // request to read the requested number of bytes to fulfill the contract, 889 // doesn't matter if we time out. We still return all the data we have available. 890 bytesReadToBuffer += internalSerialStream.Read(buffer, offset + bytesReadToBuffer, bytesLeftToRead); 891 892 decoder.Reset(); 893 return bytesReadToBuffer; 894 } 895 896 // publicly exposed "ReadOneChar"-type: Read() 897 // reads one full character from the stream ReadChar()898 public int ReadChar() 899 { 900 if (!IsOpen) 901 throw new InvalidOperationException(SR.GetString(SR.Port_not_open)); 902 903 return ReadOneChar(readTimeout); 904 } 905 906 // gets next available full character, which may be from the buffer, the stream, or both. 907 // this takes size^2 time at most, where *size* is the maximum size of any one character in an encoding. 908 // The user can call Read(1) to mimic this functionality. 909 910 // We can replace ReadOneChar with Read at some point ReadOneChar(int timeout)911 private int ReadOneChar(int timeout) 912 { 913 int nextByte; 914 int timeUsed = 0; 915 Debug.Assert(IsOpen, "ReadOneChar - port not open"); 916 917 // case 1: we have >= 1 character in the internal buffer. 918 if (decoder.GetCharCount(inBuffer, readPos, CachedBytesToRead) != 0) 919 { 920 int beginReadPos = readPos; 921 // get characters from buffer. 922 do 923 { 924 readPos++; 925 } while (decoder.GetCharCount(inBuffer, beginReadPos, readPos - beginReadPos) < 1); 926 927 try { 928 decoder.GetChars(inBuffer, beginReadPos, readPos - beginReadPos, oneChar, 0); 929 } 930 catch { 931 932 // Handle surrogate chars correctly, restore readPos 933 readPos = beginReadPos; 934 throw; 935 } 936 return oneChar[0]; 937 } 938 else 939 { 940 941 // need to return immediately. 942 if (timeout == 0) { 943 // read all bytes in the serial driver in here. Make sure we ask for at least 1 byte 944 // so that we get the proper timeout behavior 945 int bytesInStream = internalSerialStream.BytesToRead; 946 if (bytesInStream == 0) 947 bytesInStream = 1; 948 MaybeResizeBuffer(bytesInStream); 949 readLen += internalSerialStream.Read(inBuffer, readLen, bytesInStream); // read all immediately avail. 950 951 // If what we have in the buffer is not enough, throw TimeoutExc 952 // if we are reading surrogate char then ReadBufferIntoChars 953 // will throw argexc and that is okay as readPos is not altered 954 if (ReadBufferIntoChars(oneChar, 0, 1, false) == 0) 955 throw new TimeoutException(); 956 else 957 return oneChar[0]; 958 } 959 960 // case 2: we need to read from outside to find this. 961 // timeout is either infinite or positive. 962 int startTicks = Environment.TickCount; 963 do 964 { 965 if (timeout == SerialPort.InfiniteTimeout) 966 nextByte = internalSerialStream.ReadByte(InfiniteTimeout); 967 else if (timeout - timeUsed >= 0) { 968 nextByte = internalSerialStream.ReadByte(timeout - timeUsed); 969 timeUsed = Environment.TickCount - startTicks; 970 } 971 else 972 throw new TimeoutException(); 973 974 MaybeResizeBuffer(1); 975 inBuffer[readLen++] = (byte) nextByte; // we must add to the end of the buffer 976 } while (decoder.GetCharCount(inBuffer, readPos, readLen - readPos) < 1); 977 } 978 979 // If we are reading surrogate char then this will throw argexc 980 // we need not deal with that exc because we have not altered readPos yet. 981 decoder.GetChars(inBuffer, readPos, readLen - readPos, oneChar, 0); 982 983 // Everything should be out of inBuffer now. We'll just reset the pointers. 984 readLen = readPos = 0; 985 return oneChar[0]; 986 } 987 988 // Will return 'n' (1 < n < count) characters (or) TimeoutExc Read(char[] buffer, int offset, int count)989 public int Read(char[] buffer, int offset, int count) 990 { 991 if (!IsOpen) 992 throw new InvalidOperationException(SR.GetString(SR.Port_not_open)); 993 if (buffer==null) 994 throw new ArgumentNullException("buffer", SR.GetString(SR.ArgumentNull_Buffer)); 995 if (offset < 0) 996 throw new ArgumentOutOfRangeException("offset", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired)); 997 if (count < 0) 998 throw new ArgumentOutOfRangeException("count", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired)); 999 if (buffer.Length - offset < count) 1000 throw new ArgumentException(SR.GetString(SR.Argument_InvalidOffLen)); 1001 1002 return InternalRead(buffer, offset, count, readTimeout, false); 1003 } 1004 InternalRead(char[] buffer, int offset, int count, int timeout, bool countMultiByteCharsAsOne)1005 private int InternalRead(char[] buffer, int offset, int count, int timeout, bool countMultiByteCharsAsOne) 1006 { 1007 Debug.Assert(IsOpen, "port not open!"); 1008 Debug.Assert(buffer!=null, "invalid buffer!"); 1009 Debug.Assert(offset >= 0, "invalid offset!"); 1010 Debug.Assert(count >= 0, "invalid count!"); 1011 Debug.Assert(buffer.Length - offset >= count, "invalid offset/count!"); 1012 1013 if (count == 0) return 0; // immediately return on zero chars desired. This simplifies things later. 1014 1015 // Get the startticks before we read the underlying stream 1016 int startTicks = Environment.TickCount; 1017 1018 // read everything else into internal buffer, which we know we can do instantly, and see if we NOW have enough. 1019 int bytesInStream = internalSerialStream.BytesToRead; 1020 MaybeResizeBuffer(bytesInStream); 1021 readLen += internalSerialStream.Read(inBuffer, readLen, bytesInStream); // should execute instantaneously. 1022 1023 int charsWeAlreadyHave = decoder.GetCharCount(inBuffer, readPos, CachedBytesToRead); // full chars already in our buffer 1024 if (charsWeAlreadyHave > 0) 1025 { 1026 // we found some chars after reading everything the SerialStream had to offer. We'll return what we have 1027 // rather than wait for more. 1028 return ReadBufferIntoChars(buffer, offset, count, countMultiByteCharsAsOne); 1029 } 1030 1031 if (timeout == 0) 1032 throw new TimeoutException(); 1033 1034 // else: we need to do incremental reads from the stream. 1035 // ----- 1036 // our internal algorithm for finding exactly n characters is a bit complicated, but must overcome the 1037 // hurdle of NEVER READING TOO MANY BYTES from the Stream, since we can time out. A variable-length encoding 1038 // allows anywhere between minimum and maximum bytes per char times number of chars to be the exactly correct 1039 // target, and we have to take care not to overuse GetCharCount(). The problem is that GetCharCount() will never tell 1040 // us if we've read "half" a character in our current set of collected bytes; it underestimates. 1041 // size = maximum bytes per character in the encoding. n = number of characters requested. 1042 // Solution I: Use ReadOneChar() to read successive characters until we get to n. 1043 // Read calls: size * n; GetCharCount calls: size * n; each byte "counted": size times. 1044 // Solution II: Use a binary reduction and backtracking to reduce the number of calls. 1045 // Read calls: size * log n; GetCharCount calls: size * log n; each byte "counted": size * (log n) / n times. 1046 // We use the second, more complicated solution here. Note log is actually log_(size/size - 1)... 1047 1048 1049 // we need to read some from the stream 1050 // read *up to* the maximum number of bytes from the stream 1051 // we can read more since we receive everything instantaneously, and we don't have enough, 1052 // so when we do receive any data, it will be necessary and sufficient. 1053 1054 int justRead; 1055 int maxReadSize = Encoding.GetMaxByteCount(count); 1056 do { 1057 MaybeResizeBuffer(maxReadSize); 1058 1059 readLen += internalSerialStream.Read(inBuffer, readLen, maxReadSize); 1060 justRead = ReadBufferIntoChars(buffer, offset, count, countMultiByteCharsAsOne); 1061 if (justRead > 0) { 1062 return justRead; 1063 } 1064 } while (timeout == SerialPort.InfiniteTimeout || (timeout - GetElapsedTime(Environment.TickCount, startTicks) > 0)); 1065 1066 // must've timed out w/o getting a character. 1067 throw new TimeoutException(); 1068 } 1069 1070 // ReadBufferIntoChars reads from Serial Port's inBuffer up to *count* chars and 1071 // places them in *buffer* starting at *offset*. 1072 // This does not call any stream Reads, and so takes "no time". 1073 // If the buffer specified is insufficient to accommodate surrogate characters 1074 // the call to underlying Decoder.GetChars will throw argexc. ReadBufferIntoChars(char[] buffer, int offset, int count, bool countMultiByteCharsAsOne)1075 private int ReadBufferIntoChars(char[] buffer, int offset, int count, bool countMultiByteCharsAsOne) 1076 { 1077 Debug.Assert(count != 0, "Count should never be zero. We will probably see bugs further down if count is 0."); 1078 1079 int bytesToRead = Math.Min(count, CachedBytesToRead); 1080 1081 // There are lots of checks to determine if this really is a single byte encoding with no 1082 // funky fallbacks that would make it not single byte 1083 DecoderReplacementFallback fallback = encoding.DecoderFallback as DecoderReplacementFallback; 1084 if (encoding.IsSingleByte && encoding.GetMaxCharCount(bytesToRead) == bytesToRead && 1085 fallback != null && fallback.MaxCharCount == 1) 1086 { 1087 // kill ASCII/ANSI encoding easily. 1088 // read at least one and at most *count* characters 1089 decoder.GetChars(inBuffer, readPos, bytesToRead, buffer, offset); 1090 1091 readPos += bytesToRead; 1092 if (readPos == readLen) readPos = readLen = 0; 1093 return bytesToRead; 1094 } 1095 else 1096 { 1097 // 1098 // We want to turn inBuffer into at most count chars. This algorithm basically works like this: 1099 // 1) Take the largest step possible that won't give us too many chars 1100 // 2) If we find some chars, walk backwards until we find exactly how many bytes 1101 // they occupy. lastFullCharPos points to the end of the full chars. 1102 // 3) if we don't have enough chars for the buffer, goto #1 1103 1104 int totalBytesExamined = 0; // total number of Bytes in inBuffer we've looked at 1105 int totalCharsFound = 0; // total number of chars we've found in inBuffer, totalCharsFound <= totalBytesExamined 1106 int currentBytesToExamine; // the number of additional bytes to examine for characters 1107 int currentCharsFound; // the number of additional chars found after examining currentBytesToExamine extra bytes 1108 int lastFullCharPos = readPos; // first index AFTER last full char read, capped at ReadLen. 1109 do 1110 { 1111 currentBytesToExamine = Math.Min(count - totalCharsFound, readLen - readPos - totalBytesExamined); 1112 if (currentBytesToExamine <= 0) 1113 break; 1114 1115 totalBytesExamined += currentBytesToExamine; 1116 // recalculate currentBytesToExamine so that it includes leftover bytes from the last iteration. 1117 currentBytesToExamine = readPos + totalBytesExamined - lastFullCharPos; 1118 1119 // make sure we don't go beyond the end of the valid data that we have. 1120 Debug.Assert((lastFullCharPos + currentBytesToExamine) <= readLen, "We should never be attempting to read more bytes than we have"); 1121 1122 currentCharsFound = decoder.GetCharCount(inBuffer, lastFullCharPos, currentBytesToExamine); 1123 1124 if (currentCharsFound > 0) 1125 { 1126 if ((totalCharsFound + currentCharsFound) > count) { 1127 1128 // Multibyte unicode sequence (possibly surrogate chars) 1129 // at the end of the buffer. We should not split the sequence, 1130 // instead return with less chars now and defer reading them 1131 // until next time 1132 if (!countMultiByteCharsAsOne) 1133 break; 1134 1135 // If we are here it is from ReadTo which attempts to read one logical character 1136 // at a time. The supplied singleCharBuffer should be large enough to accommodate 1137 // this multi-byte char 1138 Debug.Assert((buffer.Length - offset - totalCharsFound) >= currentCharsFound, "internal buffer to read one full unicode char sequence is not sufficient!"); 1139 } 1140 1141 // go backwards until we know we have a full set of currentCharsFound bytes with no extra lead-bytes. 1142 int foundCharsByteLength = currentBytesToExamine; 1143 do 1144 { 1145 foundCharsByteLength--; 1146 } while (decoder.GetCharCount(inBuffer, lastFullCharPos, foundCharsByteLength) == currentCharsFound); 1147 1148 // Fill into destination buffer all the COMPLETE characters we've read. 1149 // If the buffer specified is insufficient to accommodate surrogate character 1150 // the call to underlying Decoder.GetChars will throw argexc. We need not 1151 // deal with this exc because we have not altered readPos yet. 1152 decoder.GetChars(inBuffer, lastFullCharPos, foundCharsByteLength + 1, buffer, offset + totalCharsFound); 1153 lastFullCharPos = lastFullCharPos + foundCharsByteLength + 1; // update the end position of last known char. 1154 } 1155 1156 totalCharsFound += currentCharsFound; 1157 } while ((totalCharsFound < count) && (totalBytesExamined < CachedBytesToRead)); 1158 1159 readPos = lastFullCharPos; 1160 1161 if (readPos == readLen) readPos = readLen = 0; 1162 return totalCharsFound; 1163 } 1164 } 1165 ReadByte()1166 public int ReadByte() 1167 { 1168 if (!IsOpen) 1169 throw new InvalidOperationException(SR.GetString(SR.Port_not_open)); 1170 if (readLen != readPos) // stuff left in buffer, so we can read from it 1171 return inBuffer[readPos++]; 1172 1173 decoder.Reset(); 1174 return internalSerialStream.ReadByte(); // otherwise, ask the stream. 1175 } 1176 ReadExisting()1177 public string ReadExisting() 1178 { 1179 if (!IsOpen) 1180 throw new InvalidOperationException(SR.GetString(SR.Port_not_open)); 1181 1182 byte [] bytesReceived = new byte[BytesToRead]; 1183 1184 if (readPos < readLen) 1185 { // stuff in internal buffer 1186 Buffer.BlockCopy(inBuffer, readPos, bytesReceived, 0, CachedBytesToRead); 1187 } 1188 internalSerialStream.Read(bytesReceived, CachedBytesToRead, bytesReceived.Length - (CachedBytesToRead)); // get everything 1189 // Read full characters and leave partial input in the buffer. Encoding.GetCharCount doesn't work because 1190 // it returns fallback characters on partial input, meaning that it overcounts. Instead, we use 1191 // GetCharCount from the decoder and tell it to preserve state, so that it returns the count of full 1192 // characters. Note that we don't actually want it to preserve state, so we call the decoder as if it's 1193 // preserving state and then call Reset in between calls. This uses a local decoder instead of the class 1194 // member decoder because that one may preserve state across SerialPort method calls. 1195 Decoder localDecoder = Encoding.GetDecoder(); 1196 int numCharsReceived = localDecoder.GetCharCount(bytesReceived, 0, bytesReceived.Length); 1197 int lastFullCharIndex = bytesReceived.Length; 1198 1199 if (numCharsReceived == 0) 1200 { 1201 Buffer.BlockCopy(bytesReceived, 0, inBuffer, 0, bytesReceived.Length); // put it all back! 1202 // don't change readPos. --> readPos == 0? 1203 readPos = 0; 1204 readLen = bytesReceived.Length; 1205 return ""; 1206 } 1207 1208 do 1209 { 1210 localDecoder.Reset(); 1211 lastFullCharIndex--; 1212 } while (localDecoder.GetCharCount(bytesReceived, 0, lastFullCharIndex) == numCharsReceived); 1213 1214 readPos = 0; 1215 readLen = bytesReceived.Length - (lastFullCharIndex + 1); 1216 1217 Buffer.BlockCopy(bytesReceived, lastFullCharIndex + 1, inBuffer, 0, bytesReceived.Length - (lastFullCharIndex + 1)); 1218 return Encoding.GetString(bytesReceived, 0, lastFullCharIndex + 1); 1219 } 1220 ReadLine()1221 public string ReadLine() { 1222 return ReadTo(NewLine); 1223 } 1224 ReadTo(string value)1225 public string ReadTo(string value) 1226 { 1227 if (!IsOpen) 1228 throw new InvalidOperationException(SR.GetString(SR.Port_not_open)); 1229 if (value == null) 1230 throw new ArgumentNullException("value"); 1231 if (value.Length == 0) 1232 throw new ArgumentException(SR.GetString(SR.InvalidNullEmptyArgument, "value")); 1233 1234 int startTicks = Environment.TickCount; 1235 int numCharsRead; 1236 int timeUsed = 0; 1237 int timeNow; 1238 StringBuilder currentLine = new StringBuilder(); 1239 char lastValueChar = value[value.Length-1]; 1240 1241 // for timeout issues, best to read everything already on the stream into our buffers. 1242 // first make sure inBuffer is big enough 1243 int bytesInStream = internalSerialStream.BytesToRead; 1244 MaybeResizeBuffer(bytesInStream); 1245 1246 readLen += internalSerialStream.Read(inBuffer, readLen, bytesInStream); 1247 int beginReadPos = readPos; 1248 1249 if (singleCharBuffer == null) { 1250 // This is somewhat of an approximate guesstimate to get the max char[] size needed to encode a single character 1251 singleCharBuffer = new char[maxByteCountForSingleChar]; 1252 } 1253 1254 try { 1255 while (true) 1256 { 1257 if(readTimeout == InfiniteTimeout) { 1258 numCharsRead = InternalRead(singleCharBuffer, 0, 1, readTimeout, true); 1259 } 1260 else if (readTimeout - timeUsed >= 0) { 1261 timeNow = Environment.TickCount; 1262 numCharsRead = InternalRead(singleCharBuffer, 0, 1, readTimeout - timeUsed, true); 1263 timeUsed += Environment.TickCount - timeNow; 1264 } 1265 else 1266 throw new TimeoutException(); 1267 1268 #if _DEBUG 1269 if (numCharsRead > 1) { 1270 for (int i=0; i<numCharsRead; i++) 1271 Debug.Assert((Char.IsSurrogate(singleCharBuffer[i])), "number of chars read should be more than one only for surrogate characters!"); 1272 } 1273 #endif 1274 Debug.Assert((numCharsRead > 0), "possible bug in ReadBufferIntoChars, reading surrogate char?"); 1275 currentLine.Append(singleCharBuffer, 0, numCharsRead); 1276 1277 if (lastValueChar == (char) singleCharBuffer[numCharsRead-1] && (currentLine.Length >= value.Length)) { 1278 // we found the last char in the value string. See if the rest is there. No need to 1279 // recompare the last char of the value string. 1280 bool found = true; 1281 for (int i=2; i<=value.Length; i++) { 1282 if (value[value.Length-i] != currentLine[currentLine.Length-i]) { 1283 found = false; 1284 break; 1285 } 1286 } 1287 1288 if (found) { 1289 // we found the search string. Exclude it from the return string. 1290 string ret = currentLine.ToString(0, currentLine.Length - value.Length); 1291 if (readPos == readLen) readPos = readLen = 0; 1292 return ret; 1293 } 1294 } 1295 } 1296 } 1297 catch { 1298 // We probably got here due to timeout. 1299 // We will try our best to restore the internal states, it's tricky! 1300 1301 // 0) Save any existing data 1302 // 1) Restore readPos to the original position upon entering ReadTo 1303 // 2) Set readLen to the number of bytes read since entering ReadTo 1304 // 3) Restore inBuffer so that it contains the bytes from currentLine, resizing if necessary. 1305 // 4) Append the buffer with any saved data from 0) 1306 1307 byte[] readBuffer = encoding.GetBytes(currentLine.ToString()); 1308 1309 // We will compact the data by default 1310 if (readBuffer.Length > 0) { 1311 int bytesToSave = CachedBytesToRead; 1312 byte[] savBuffer = new byte[bytesToSave]; 1313 1314 if (bytesToSave > 0) 1315 Buffer.BlockCopy(inBuffer, readPos, savBuffer, 0, bytesToSave); 1316 1317 readPos = 0; 1318 readLen = 0; 1319 1320 MaybeResizeBuffer(readBuffer.Length + bytesToSave); 1321 1322 Buffer.BlockCopy(readBuffer, 0, inBuffer, readLen, readBuffer.Length); 1323 readLen += readBuffer.Length; 1324 1325 if (bytesToSave > 0) { 1326 Buffer.BlockCopy(savBuffer, 0, inBuffer, readLen, bytesToSave); 1327 readLen += bytesToSave; 1328 } 1329 } 1330 1331 throw; 1332 } 1333 } 1334 1335 // Writes string to output, no matter string's length. Write(string text)1336 public void Write(string text) 1337 { 1338 if (!IsOpen) 1339 throw new InvalidOperationException(SR.GetString(SR.Port_not_open)); 1340 if (text == null) 1341 throw new ArgumentNullException("text"); 1342 if (text.Length == 0) return; 1343 byte [] bytesToWrite; 1344 1345 bytesToWrite = encoding.GetBytes(text); 1346 1347 internalSerialStream.Write(bytesToWrite, 0, bytesToWrite.Length, writeTimeout); 1348 } 1349 1350 // encoding-dependent Write-chars method. 1351 // Probably as performant as direct conversion from ASCII to bytes, since we have to cast anyway (we can just call GetBytes) Write(char[] buffer, int offset, int count)1352 public void Write(char[] buffer, int offset, int count) { 1353 if (!IsOpen) 1354 throw new InvalidOperationException(SR.GetString(SR.Port_not_open)); 1355 if (buffer == null) 1356 throw new ArgumentNullException("buffer"); 1357 if (offset < 0) 1358 throw new ArgumentOutOfRangeException("offset", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired)); 1359 if (count < 0) 1360 throw new ArgumentOutOfRangeException("count", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired)); 1361 if (buffer.Length - offset < count) 1362 throw new ArgumentException(SR.GetString(SR.Argument_InvalidOffLen)); 1363 1364 if (buffer.Length == 0) return; 1365 1366 byte [] byteArray = Encoding.GetBytes(buffer,offset, count); 1367 Write(byteArray, 0, byteArray.Length); 1368 1369 } 1370 1371 // Writes a specified section of a byte buffer to output. Write(byte[] buffer, int offset, int count)1372 public void Write(byte[] buffer, int offset, int count) 1373 { 1374 if (!IsOpen) 1375 throw new InvalidOperationException(SR.GetString(SR.Port_not_open)); 1376 if (buffer==null) 1377 throw new ArgumentNullException("buffer", SR.GetString(SR.ArgumentNull_Buffer)); 1378 if (offset < 0) 1379 throw new ArgumentOutOfRangeException("offset", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired)); 1380 if (count < 0) 1381 throw new ArgumentOutOfRangeException("count", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNumRequired)); 1382 if (buffer.Length - offset < count) 1383 throw new ArgumentException(SR.GetString(SR.Argument_InvalidOffLen)); 1384 if (buffer.Length == 0) return; 1385 1386 internalSerialStream.Write(buffer, offset, count, writeTimeout); 1387 } 1388 WriteLine(string text)1389 public void WriteLine(string text) { 1390 Write(text + NewLine); 1391 } 1392 1393 1394 // ----- SECTION: internal utility methods ----------------* 1395 1396 // included here just to use the event filter to block unwanted invocations of the Serial Port's events. 1397 // Plus, this enforces the requirement on the received event that the number of buffered bytes >= receivedBytesThreshold CatchErrorEvents(object src, SerialErrorReceivedEventArgs e)1398 private void CatchErrorEvents(object src, SerialErrorReceivedEventArgs e) 1399 { 1400 SerialErrorReceivedEventHandler eventHandler = ErrorReceived; 1401 SerialStream stream = internalSerialStream; 1402 1403 if ((eventHandler != null) && (stream != null)){ 1404 lock (stream) { 1405 if (stream.IsOpen) 1406 eventHandler(this, e); 1407 } 1408 } 1409 } 1410 CatchPinChangedEvents(object src, SerialPinChangedEventArgs e)1411 private void CatchPinChangedEvents(object src, SerialPinChangedEventArgs e) 1412 { 1413 SerialPinChangedEventHandler eventHandler = PinChanged; 1414 SerialStream stream = internalSerialStream; 1415 1416 if ((eventHandler != null) && (stream != null)){ 1417 lock (stream) { 1418 if (stream.IsOpen) 1419 eventHandler(this, e); 1420 } 1421 } 1422 } 1423 CatchReceivedEvents(object src, SerialDataReceivedEventArgs e)1424 private void CatchReceivedEvents(object src, SerialDataReceivedEventArgs e) 1425 { 1426 SerialDataReceivedEventHandler eventHandler = DataReceived; 1427 SerialStream stream = internalSerialStream; 1428 1429 if ((eventHandler != null) && (stream != null)){ 1430 lock (stream) { 1431 // SerialStream might be closed between the time the event runner 1432 // pumped this event and the time the threadpool thread end up 1433 // invoking this event handler. The above lock and IsOpen check 1434 // ensures that we raise the event only when the port is open 1435 1436 bool raiseEvent = false; 1437 try { 1438 raiseEvent = stream.IsOpen && (SerialData.Eof == e.EventType || BytesToRead >= receivedBytesThreshold); 1439 } 1440 catch { 1441 // Ignore and continue. SerialPort might have been closed already! 1442 } 1443 finally { 1444 if (raiseEvent) 1445 eventHandler(this, e); // here, do your reading, etc. 1446 } 1447 } 1448 } 1449 } 1450 CompactBuffer()1451 private void CompactBuffer() 1452 { 1453 Buffer.BlockCopy(inBuffer, readPos, inBuffer, 0, CachedBytesToRead); 1454 readLen = CachedBytesToRead; 1455 readPos = 0; 1456 } 1457 1458 // This method guarantees that our inBuffer is big enough. The parameter passed in is 1459 // the number of bytes that our code is going to add to inBuffer. MaybeResizeBuffer will 1460 // do one of three things depending on how much data is already in the buffer and how 1461 // much will be added: 1462 // 1) Nothing. The current buffer is big enough to hold it all 1463 // 2) Compact the existing data and keep the current buffer. 1464 // 3) Create a new, larger buffer and compact the existing data into it. MaybeResizeBuffer(int additionalByteLength)1465 private void MaybeResizeBuffer(int additionalByteLength) 1466 { 1467 // Case 1. No action needed 1468 if (additionalByteLength + readLen <= inBuffer.Length) 1469 return; 1470 1471 // Case 2. Compact 1472 if (CachedBytesToRead + additionalByteLength <= inBuffer.Length / 2) 1473 CompactBuffer(); 1474 else { 1475 // Case 3. Create a new buffer 1476 int newLength = Math.Max(CachedBytesToRead + additionalByteLength, inBuffer.Length * 2); 1477 1478 Debug.Assert(inBuffer.Length >= readLen, "ResizeBuffer - readLen > inBuffer.Length"); 1479 byte[] newBuffer = new byte[newLength]; 1480 // only copy the valid data from inBuffer, and put it at the beginning of newBuffer. 1481 Buffer.BlockCopy(inBuffer, readPos, newBuffer, 0, CachedBytesToRead); 1482 readLen = CachedBytesToRead; 1483 readPos = 0; 1484 inBuffer = newBuffer; 1485 } 1486 } 1487 GetElapsedTime(int currentTickCount, int startTickCount)1488 private static int GetElapsedTime(int currentTickCount, int startTickCount) 1489 { 1490 int elapsedTime = unchecked(currentTickCount - startTickCount); 1491 return (elapsedTime >= 0) ? (int)elapsedTime : Int32.MaxValue; 1492 } 1493 } 1494 } 1495