1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991
|
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
/*=============================================================================
**
** Class: SerialStream
**
** Purpose: Class for enabling low-level sync 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.Diagnostics.Contracts;
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.
// * Lifetime of the COM port's handle is controlled via a SafeHandle. Thus, all properties are available
// * only when the SerialStream is open and not disposed.
// * 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. A customer also reported seeing ERROR_BAD_COMMAND here.
// Do not throw an exception on the finalizer thread - that's just rude,
// since apps can't catch it and we may tear down the app.
const int ERROR_DEVICE_REMOVED = 1617;
if ((hr == NativeMethods.ERROR_ACCESS_DENIED || hr == NativeMethods.ERROR_BAD_COMMAND || hr == ERROR_DEVICE_REMOVED) && !disposing) {
skipSPAccess = true;
}
else {
// should not happen
Contract.Assert(false, String.Format("Unexpected error code from EscapeCommFunction in SerialPort.Dispose(bool) Error code: 0x{0:x}", (uint)hr));
// Do not throw an exception from the finalizer here.
if (disposing)
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
private readonly 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();
// When a device is disconnected unexpectedly from a serial port, there appear to be
// at least three error codes Windows or drivers may return.
const int ERROR_DEVICE_REMOVED = 1617;
if (hr == NativeMethods.ERROR_ACCESS_DENIED || hr == NativeMethods.ERROR_BAD_COMMAND || hr == ERROR_DEVICE_REMOVED) {
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);
}
} // while (!ShutdownLoop)
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; }
}
}
}
}
|