This is the latest working repository used in our demo video for the Maxim to display temperature readings on Bluetooth
hspguisourcev301/HspGuiSourceV301/GuiDLLs/SerialWrap/serialportio.cs@5:bc128a16232f, 2021-05-02 (annotated)
- Committer:
- darienf
- Date:
- Sun May 02 23:09:04 2021 +0000
- Revision:
- 5:bc128a16232f
- Parent:
- 3:36de8b9e4b1a
This is the program that was last used, that has the working temperature and some comments
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
darienf | 3:36de8b9e4b1a | 1 | using System; |
darienf | 3:36de8b9e4b1a | 2 | using System.Collections.Generic; |
darienf | 3:36de8b9e4b1a | 3 | using System.Linq; |
darienf | 3:36de8b9e4b1a | 4 | using System.Text; |
darienf | 3:36de8b9e4b1a | 5 | using System.Threading.Tasks; |
darienf | 3:36de8b9e4b1a | 6 | using System.Threading; |
darienf | 3:36de8b9e4b1a | 7 | using System.Reflection; |
darienf | 3:36de8b9e4b1a | 8 | using System.IO; |
darienf | 3:36de8b9e4b1a | 9 | |
darienf | 3:36de8b9e4b1a | 10 | using System.IO.Ports; |
darienf | 3:36de8b9e4b1a | 11 | using SerialPortTester; |
darienf | 3:36de8b9e4b1a | 12 | |
darienf | 3:36de8b9e4b1a | 13 | using Common.Logging; |
darienf | 3:36de8b9e4b1a | 14 | |
darienf | 3:36de8b9e4b1a | 15 | namespace SerialWrap |
darienf | 3:36de8b9e4b1a | 16 | { |
darienf | 3:36de8b9e4b1a | 17 | public interface ISerialPortIo : IDisposable |
darienf | 3:36de8b9e4b1a | 18 | { |
darienf | 3:36de8b9e4b1a | 19 | string PortName { get; } |
darienf | 3:36de8b9e4b1a | 20 | string ReadLine(); |
darienf | 3:36de8b9e4b1a | 21 | void WriteLine(string text); |
darienf | 3:36de8b9e4b1a | 22 | } |
darienf | 3:36de8b9e4b1a | 23 | |
darienf | 3:36de8b9e4b1a | 24 | public class SerialPortConfig |
darienf | 3:36de8b9e4b1a | 25 | { |
darienf | 3:36de8b9e4b1a | 26 | public string Name { get; private set; } |
darienf | 3:36de8b9e4b1a | 27 | public int BaudRate { get; private set; } |
darienf | 3:36de8b9e4b1a | 28 | public int DataBits { get; private set; } |
darienf | 3:36de8b9e4b1a | 29 | public StopBits StopBits { get; private set; } |
darienf | 3:36de8b9e4b1a | 30 | public Parity Parity { get; private set; } |
darienf | 3:36de8b9e4b1a | 31 | public bool DtrEnable { get; private set; } |
darienf | 3:36de8b9e4b1a | 32 | public bool RtsEnable { get; private set; } |
darienf | 3:36de8b9e4b1a | 33 | |
darienf | 3:36de8b9e4b1a | 34 | public SerialPortConfig( |
darienf | 3:36de8b9e4b1a | 35 | string name, |
darienf | 3:36de8b9e4b1a | 36 | int baudRate, |
darienf | 3:36de8b9e4b1a | 37 | int dataBits, |
darienf | 3:36de8b9e4b1a | 38 | StopBits stopBits, |
darienf | 3:36de8b9e4b1a | 39 | Parity parity, |
darienf | 3:36de8b9e4b1a | 40 | bool dtrEnable, |
darienf | 3:36de8b9e4b1a | 41 | bool rtsEnable) |
darienf | 3:36de8b9e4b1a | 42 | { |
darienf | 3:36de8b9e4b1a | 43 | if (String.IsNullOrWhiteSpace(name)) throw new ArgumentNullException("name"); |
darienf | 3:36de8b9e4b1a | 44 | |
darienf | 3:36de8b9e4b1a | 45 | this.RtsEnable = rtsEnable; |
darienf | 3:36de8b9e4b1a | 46 | this.BaudRate = baudRate; |
darienf | 3:36de8b9e4b1a | 47 | this.DataBits = dataBits; |
darienf | 3:36de8b9e4b1a | 48 | this.StopBits = stopBits; |
darienf | 3:36de8b9e4b1a | 49 | this.Parity = parity; |
darienf | 3:36de8b9e4b1a | 50 | this.DtrEnable = dtrEnable; |
darienf | 3:36de8b9e4b1a | 51 | this.Name = name; |
darienf | 3:36de8b9e4b1a | 52 | } |
darienf | 3:36de8b9e4b1a | 53 | |
darienf | 3:36de8b9e4b1a | 54 | public override string ToString() |
darienf | 3:36de8b9e4b1a | 55 | { |
darienf | 3:36de8b9e4b1a | 56 | return String.Format( |
darienf | 3:36de8b9e4b1a | 57 | "{0} (Baud: {1}/DataBits: {2}/Parity: {3}/StopBits: {4}/{5})", |
darienf | 3:36de8b9e4b1a | 58 | this.Name, |
darienf | 3:36de8b9e4b1a | 59 | this.BaudRate, |
darienf | 3:36de8b9e4b1a | 60 | this.DataBits, |
darienf | 3:36de8b9e4b1a | 61 | this.Parity, |
darienf | 3:36de8b9e4b1a | 62 | this.StopBits, |
darienf | 3:36de8b9e4b1a | 63 | this.RtsEnable ? "RTS" : "No RTS"); |
darienf | 3:36de8b9e4b1a | 64 | } |
darienf | 3:36de8b9e4b1a | 65 | } |
darienf | 3:36de8b9e4b1a | 66 | |
darienf | 3:36de8b9e4b1a | 67 | // Wrapper around SerialPort |
darienf | 3:36de8b9e4b1a | 68 | public class SerialPortIo : ISerialPortIo |
darienf | 3:36de8b9e4b1a | 69 | { |
darienf | 3:36de8b9e4b1a | 70 | protected ILog Log { get; private set; } |
darienf | 3:36de8b9e4b1a | 71 | static readonly ILog s_Log = LogManager.GetLogger(typeof(SerialPortIo)); |
darienf | 3:36de8b9e4b1a | 72 | |
darienf | 3:36de8b9e4b1a | 73 | readonly SerialPort _port; |
darienf | 3:36de8b9e4b1a | 74 | readonly Stream _internalSerialStream; |
darienf | 3:36de8b9e4b1a | 75 | |
darienf | 3:36de8b9e4b1a | 76 | private int readTimeout; |
darienf | 3:36de8b9e4b1a | 77 | private int writeTimeout; |
darienf | 3:36de8b9e4b1a | 78 | |
darienf | 3:36de8b9e4b1a | 79 | public int ReadTimeout |
darienf | 3:36de8b9e4b1a | 80 | { |
darienf | 3:36de8b9e4b1a | 81 | get { return readTimeout; } |
darienf | 3:36de8b9e4b1a | 82 | set |
darienf | 3:36de8b9e4b1a | 83 | { |
darienf | 3:36de8b9e4b1a | 84 | readTimeout = value; |
darienf | 3:36de8b9e4b1a | 85 | _port.ReadTimeout = readTimeout; |
darienf | 3:36de8b9e4b1a | 86 | } |
darienf | 3:36de8b9e4b1a | 87 | } |
darienf | 3:36de8b9e4b1a | 88 | |
darienf | 3:36de8b9e4b1a | 89 | public int WriteTimeout |
darienf | 3:36de8b9e4b1a | 90 | { |
darienf | 3:36de8b9e4b1a | 91 | get { return writeTimeout; } |
darienf | 3:36de8b9e4b1a | 92 | set |
darienf | 3:36de8b9e4b1a | 93 | { |
darienf | 3:36de8b9e4b1a | 94 | writeTimeout = value; |
darienf | 3:36de8b9e4b1a | 95 | _port.WriteTimeout = writeTimeout; |
darienf | 3:36de8b9e4b1a | 96 | } |
darienf | 3:36de8b9e4b1a | 97 | } |
darienf | 3:36de8b9e4b1a | 98 | |
darienf | 3:36de8b9e4b1a | 99 | public void DiscardInBuffer() |
darienf | 3:36de8b9e4b1a | 100 | { |
darienf | 3:36de8b9e4b1a | 101 | _port.DiscardInBuffer(); |
darienf | 3:36de8b9e4b1a | 102 | } |
darienf | 3:36de8b9e4b1a | 103 | |
darienf | 3:36de8b9e4b1a | 104 | public bool IsOpen |
darienf | 3:36de8b9e4b1a | 105 | { |
darienf | 3:36de8b9e4b1a | 106 | get { return _port.IsOpen; } |
darienf | 3:36de8b9e4b1a | 107 | } |
darienf | 3:36de8b9e4b1a | 108 | |
darienf | 3:36de8b9e4b1a | 109 | public SerialPortIo(SerialPortConfig portConfig) |
darienf | 3:36de8b9e4b1a | 110 | { |
darienf | 3:36de8b9e4b1a | 111 | if (portConfig == null) throw new ArgumentNullException("portConfig"); |
darienf | 3:36de8b9e4b1a | 112 | |
darienf | 3:36de8b9e4b1a | 113 | this.Log = LogManager.GetLogger(this.GetType()); |
darienf | 3:36de8b9e4b1a | 114 | |
darienf | 3:36de8b9e4b1a | 115 | // http://zachsaw.blogspot.com/2010/07/net-serialport-woes.html |
darienf | 3:36de8b9e4b1a | 116 | SerialPortFixer.Execute(portConfig.Name); |
darienf | 3:36de8b9e4b1a | 117 | |
darienf | 3:36de8b9e4b1a | 118 | var port = new SerialPort( |
darienf | 3:36de8b9e4b1a | 119 | portConfig.Name, |
darienf | 3:36de8b9e4b1a | 120 | portConfig.BaudRate, |
darienf | 3:36de8b9e4b1a | 121 | portConfig.Parity, |
darienf | 3:36de8b9e4b1a | 122 | portConfig.DataBits, |
darienf | 3:36de8b9e4b1a | 123 | portConfig.StopBits) |
darienf | 3:36de8b9e4b1a | 124 | { |
darienf | 3:36de8b9e4b1a | 125 | RtsEnable = portConfig.RtsEnable, |
darienf | 3:36de8b9e4b1a | 126 | DtrEnable = portConfig.DtrEnable, |
darienf | 3:36de8b9e4b1a | 127 | ReadTimeout = 5000, |
darienf | 3:36de8b9e4b1a | 128 | WriteTimeout = 5000 |
darienf | 3:36de8b9e4b1a | 129 | }; |
darienf | 3:36de8b9e4b1a | 130 | port.Open(); |
darienf | 3:36de8b9e4b1a | 131 | |
darienf | 3:36de8b9e4b1a | 132 | try |
darienf | 3:36de8b9e4b1a | 133 | { |
darienf | 3:36de8b9e4b1a | 134 | this._internalSerialStream = port.BaseStream; |
darienf | 3:36de8b9e4b1a | 135 | this._port = port; |
darienf | 3:36de8b9e4b1a | 136 | this._port.DiscardInBuffer(); |
darienf | 3:36de8b9e4b1a | 137 | this._port.DiscardOutBuffer(); |
darienf | 3:36de8b9e4b1a | 138 | } |
darienf | 3:36de8b9e4b1a | 139 | catch (Exception ex) |
darienf | 3:36de8b9e4b1a | 140 | { |
darienf | 3:36de8b9e4b1a | 141 | Stream internalStream = this._internalSerialStream; |
darienf | 3:36de8b9e4b1a | 142 | |
darienf | 3:36de8b9e4b1a | 143 | if (internalStream == null) |
darienf | 3:36de8b9e4b1a | 144 | { |
darienf | 3:36de8b9e4b1a | 145 | FieldInfo field = typeof(SerialPort).GetField( |
darienf | 3:36de8b9e4b1a | 146 | "internalSerialStream", |
darienf | 3:36de8b9e4b1a | 147 | BindingFlags.Instance | BindingFlags.NonPublic); |
darienf | 3:36de8b9e4b1a | 148 | |
darienf | 3:36de8b9e4b1a | 149 | // This will happen if the SerialPort class is changed |
darienf | 3:36de8b9e4b1a | 150 | // in future versions of the .NET Framework |
darienf | 3:36de8b9e4b1a | 151 | if (field == null) |
darienf | 3:36de8b9e4b1a | 152 | { |
darienf | 3:36de8b9e4b1a | 153 | this.Log.WarnFormat( |
darienf | 3:36de8b9e4b1a | 154 | "An exception occured while creating the serial port adaptor, " |
darienf | 3:36de8b9e4b1a | 155 | + "the internal stream reference was not acquired and we were unable " |
darienf | 3:36de8b9e4b1a | 156 | + "to get it using reflection. The serial port may not be accessible " |
darienf | 3:36de8b9e4b1a | 157 | + "any further until the serial port object finalizer has been run: {0}", |
darienf | 3:36de8b9e4b1a | 158 | ex); |
darienf | 3:36de8b9e4b1a | 159 | |
darienf | 3:36de8b9e4b1a | 160 | throw; |
darienf | 3:36de8b9e4b1a | 161 | } |
darienf | 3:36de8b9e4b1a | 162 | |
darienf | 3:36de8b9e4b1a | 163 | internalStream = (Stream)field.GetValue(port); |
darienf | 3:36de8b9e4b1a | 164 | } |
darienf | 3:36de8b9e4b1a | 165 | |
darienf | 3:36de8b9e4b1a | 166 | this.Log.DebugFormat( |
darienf | 3:36de8b9e4b1a | 167 | "An error occurred while constructing the serial port adaptor: {0}", ex); |
darienf | 3:36de8b9e4b1a | 168 | |
darienf | 3:36de8b9e4b1a | 169 | SafeDisconnect(port, internalStream); |
darienf | 3:36de8b9e4b1a | 170 | throw; |
darienf | 3:36de8b9e4b1a | 171 | } |
darienf | 3:36de8b9e4b1a | 172 | } |
darienf | 3:36de8b9e4b1a | 173 | |
darienf | 3:36de8b9e4b1a | 174 | public string PortName |
darienf | 3:36de8b9e4b1a | 175 | { |
darienf | 3:36de8b9e4b1a | 176 | get { return this._port.PortName; } |
darienf | 3:36de8b9e4b1a | 177 | } |
darienf | 3:36de8b9e4b1a | 178 | |
darienf | 3:36de8b9e4b1a | 179 | public int Read(char[] buffer, int offset, int count) |
darienf | 3:36de8b9e4b1a | 180 | { |
darienf | 3:36de8b9e4b1a | 181 | return this._port.Read(buffer, offset, count); |
darienf | 3:36de8b9e4b1a | 182 | } |
darienf | 3:36de8b9e4b1a | 183 | |
darienf | 3:36de8b9e4b1a | 184 | public int Read(byte[] buffer, int offset, int count) |
darienf | 3:36de8b9e4b1a | 185 | { |
darienf | 3:36de8b9e4b1a | 186 | return this._port.Read(buffer, offset, count); |
darienf | 3:36de8b9e4b1a | 187 | } |
darienf | 3:36de8b9e4b1a | 188 | |
darienf | 3:36de8b9e4b1a | 189 | public string ReadLine() |
darienf | 3:36de8b9e4b1a | 190 | { |
darienf | 3:36de8b9e4b1a | 191 | //return this._port.ReadTo(Environment.NewLine); |
darienf | 3:36de8b9e4b1a | 192 | return this._port.ReadLine(); |
darienf | 3:36de8b9e4b1a | 193 | } |
darienf | 3:36de8b9e4b1a | 194 | |
darienf | 3:36de8b9e4b1a | 195 | public string ReadExisting() |
darienf | 3:36de8b9e4b1a | 196 | { |
darienf | 3:36de8b9e4b1a | 197 | return this._port.ReadExisting(); |
darienf | 3:36de8b9e4b1a | 198 | } |
darienf | 3:36de8b9e4b1a | 199 | |
darienf | 3:36de8b9e4b1a | 200 | public void Write(string text) |
darienf | 3:36de8b9e4b1a | 201 | { |
darienf | 3:36de8b9e4b1a | 202 | this._port.Write(text); |
darienf | 3:36de8b9e4b1a | 203 | } |
darienf | 3:36de8b9e4b1a | 204 | |
darienf | 3:36de8b9e4b1a | 205 | public void Write(char[] buffer, int offset, int count) |
darienf | 3:36de8b9e4b1a | 206 | { |
darienf | 3:36de8b9e4b1a | 207 | this._port.Write(buffer, offset, count); |
darienf | 3:36de8b9e4b1a | 208 | } |
darienf | 3:36de8b9e4b1a | 209 | |
darienf | 3:36de8b9e4b1a | 210 | |
darienf | 3:36de8b9e4b1a | 211 | public void WriteLine(string text) |
darienf | 3:36de8b9e4b1a | 212 | { |
darienf | 3:36de8b9e4b1a | 213 | //this._port.Write(text); |
darienf | 3:36de8b9e4b1a | 214 | //this._port.Write("\r"); |
darienf | 3:36de8b9e4b1a | 215 | this._port.WriteLine(text); |
darienf | 3:36de8b9e4b1a | 216 | } |
darienf | 3:36de8b9e4b1a | 217 | |
darienf | 3:36de8b9e4b1a | 218 | public void Dispose() |
darienf | 3:36de8b9e4b1a | 219 | { |
darienf | 3:36de8b9e4b1a | 220 | this.Dispose(true); |
darienf | 3:36de8b9e4b1a | 221 | } |
darienf | 3:36de8b9e4b1a | 222 | |
darienf | 3:36de8b9e4b1a | 223 | protected void Dispose(bool disposing) |
darienf | 3:36de8b9e4b1a | 224 | { |
darienf | 3:36de8b9e4b1a | 225 | SafeDisconnect(this._port, this._internalSerialStream); |
darienf | 3:36de8b9e4b1a | 226 | |
darienf | 3:36de8b9e4b1a | 227 | if (disposing) |
darienf | 3:36de8b9e4b1a | 228 | { |
darienf | 3:36de8b9e4b1a | 229 | GC.SuppressFinalize(this); |
darienf | 3:36de8b9e4b1a | 230 | } |
darienf | 3:36de8b9e4b1a | 231 | } |
darienf | 3:36de8b9e4b1a | 232 | |
darienf | 3:36de8b9e4b1a | 233 | /// <summary> |
darienf | 3:36de8b9e4b1a | 234 | /// Safely closes a serial port and its internal stream even if |
darienf | 3:36de8b9e4b1a | 235 | /// a USB serial interface was physically removed from the system |
darienf | 3:36de8b9e4b1a | 236 | /// in a reliable manner. |
darienf | 3:36de8b9e4b1a | 237 | /// </summary> |
darienf | 3:36de8b9e4b1a | 238 | /// <param name="port"></param> |
darienf | 3:36de8b9e4b1a | 239 | /// <param name="internalSerialStream"></param> |
darienf | 3:36de8b9e4b1a | 240 | /// <remarks> |
darienf | 3:36de8b9e4b1a | 241 | /// The <see cref="SerialPort"/> class has 3 different problems in disposal |
darienf | 3:36de8b9e4b1a | 242 | /// in case of a USB serial device that is physically removed: |
darienf | 3:36de8b9e4b1a | 243 | /// |
darienf | 3:36de8b9e4b1a | 244 | /// 1. The eventLoopRunner is asked to stop and <see cref="SerialPort.IsOpen"/> |
darienf | 3:36de8b9e4b1a | 245 | /// returns false. Upon disposal this property is checked and closing |
darienf | 3:36de8b9e4b1a | 246 | /// the internal serial stream is skipped, thus keeping the original |
darienf | 3:36de8b9e4b1a | 247 | /// handle open indefinitely (until the finalizer runs which leads to the next problem) |
darienf | 3:36de8b9e4b1a | 248 | /// |
darienf | 3:36de8b9e4b1a | 249 | /// The solution for this one is to manually close the internal serial stream. |
darienf | 3:36de8b9e4b1a | 250 | /// We can get its reference by <see cref="SerialPort.BaseStream" /> |
darienf | 3:36de8b9e4b1a | 251 | /// before the exception has happened or by reflection and getting the |
darienf | 3:36de8b9e4b1a | 252 | /// "internalSerialStream" field. |
darienf | 3:36de8b9e4b1a | 253 | /// |
darienf | 3:36de8b9e4b1a | 254 | /// 2. Closing the internal serial stream throws an exception and closes |
darienf | 3:36de8b9e4b1a | 255 | /// the internal handle without waiting for its eventLoopRunner thread to finish, |
darienf | 3:36de8b9e4b1a | 256 | /// causing an uncatchable ObjectDisposedException from it later on when the finalizer |
darienf | 3:36de8b9e4b1a | 257 | /// runs (which oddly avoids throwing the exception but still fails to wait for |
darienf | 3:36de8b9e4b1a | 258 | /// the eventLoopRunner). |
darienf | 3:36de8b9e4b1a | 259 | /// |
darienf | 3:36de8b9e4b1a | 260 | /// The solution is to manually ask the event loop runner thread to shutdown |
darienf | 3:36de8b9e4b1a | 261 | /// (via reflection) and waiting for it before closing the internal serial stream. |
darienf | 3:36de8b9e4b1a | 262 | /// |
darienf | 3:36de8b9e4b1a | 263 | /// 3. Since Dispose throws exceptions, the finalizer is not suppressed. |
darienf | 3:36de8b9e4b1a | 264 | /// |
darienf | 3:36de8b9e4b1a | 265 | /// The solution is to suppress their finalizers at the beginning. |
darienf | 3:36de8b9e4b1a | 266 | /// </remarks> |
darienf | 3:36de8b9e4b1a | 267 | static void SafeDisconnect(SerialPort port, Stream internalSerialStream) |
darienf | 3:36de8b9e4b1a | 268 | { |
darienf | 3:36de8b9e4b1a | 269 | GC.SuppressFinalize(port); |
darienf | 3:36de8b9e4b1a | 270 | GC.SuppressFinalize(internalSerialStream); |
darienf | 3:36de8b9e4b1a | 271 | |
darienf | 3:36de8b9e4b1a | 272 | ShutdownEventLoopHandler(internalSerialStream); |
darienf | 3:36de8b9e4b1a | 273 | |
darienf | 3:36de8b9e4b1a | 274 | try |
darienf | 3:36de8b9e4b1a | 275 | { |
darienf | 3:36de8b9e4b1a | 276 | s_Log.DebugFormat("Disposing internal serial stream"); |
darienf | 3:36de8b9e4b1a | 277 | internalSerialStream.Close(); |
darienf | 3:36de8b9e4b1a | 278 | } |
darienf | 3:36de8b9e4b1a | 279 | catch (Exception ex) |
darienf | 3:36de8b9e4b1a | 280 | { |
darienf | 3:36de8b9e4b1a | 281 | s_Log.DebugFormat( |
darienf | 3:36de8b9e4b1a | 282 | "Exception in serial stream shutdown of port {0}: {1}", port.PortName, ex); |
darienf | 3:36de8b9e4b1a | 283 | } |
darienf | 3:36de8b9e4b1a | 284 | |
darienf | 3:36de8b9e4b1a | 285 | try |
darienf | 3:36de8b9e4b1a | 286 | { |
darienf | 3:36de8b9e4b1a | 287 | s_Log.DebugFormat("Disposing serial port"); |
darienf | 3:36de8b9e4b1a | 288 | port.Close(); |
darienf | 3:36de8b9e4b1a | 289 | } |
darienf | 3:36de8b9e4b1a | 290 | catch (Exception ex) |
darienf | 3:36de8b9e4b1a | 291 | { |
darienf | 3:36de8b9e4b1a | 292 | s_Log.DebugFormat("Exception in port {0} shutdown: {1}", port.PortName, ex); |
darienf | 3:36de8b9e4b1a | 293 | } |
darienf | 3:36de8b9e4b1a | 294 | } |
darienf | 3:36de8b9e4b1a | 295 | |
darienf | 3:36de8b9e4b1a | 296 | static void ShutdownEventLoopHandler(Stream internalSerialStream) |
darienf | 3:36de8b9e4b1a | 297 | { |
darienf | 3:36de8b9e4b1a | 298 | try |
darienf | 3:36de8b9e4b1a | 299 | { |
darienf | 3:36de8b9e4b1a | 300 | s_Log.DebugFormat("Working around .NET SerialPort class Dispose bug"); |
darienf | 3:36de8b9e4b1a | 301 | |
darienf | 3:36de8b9e4b1a | 302 | FieldInfo eventRunnerField = internalSerialStream.GetType() |
darienf | 3:36de8b9e4b1a | 303 | .GetField("eventRunner", BindingFlags.NonPublic | BindingFlags.Instance); |
darienf | 3:36de8b9e4b1a | 304 | |
darienf | 3:36de8b9e4b1a | 305 | if (eventRunnerField == null) |
darienf | 3:36de8b9e4b1a | 306 | { |
darienf | 3:36de8b9e4b1a | 307 | s_Log.WarnFormat( |
darienf | 3:36de8b9e4b1a | 308 | "Unable to find EventLoopRunner field. " |
darienf | 3:36de8b9e4b1a | 309 | + "SerialPort workaround failure. Application may crash after " |
darienf | 3:36de8b9e4b1a | 310 | + "disposing SerialPort unless .NET 1.1 unhandled exception " |
darienf | 3:36de8b9e4b1a | 311 | + "policy is enabled from the application's config file."); |
darienf | 3:36de8b9e4b1a | 312 | } |
darienf | 3:36de8b9e4b1a | 313 | else |
darienf | 3:36de8b9e4b1a | 314 | { |
darienf | 3:36de8b9e4b1a | 315 | object eventRunner = eventRunnerField.GetValue(internalSerialStream); |
darienf | 3:36de8b9e4b1a | 316 | Type eventRunnerType = eventRunner.GetType(); |
darienf | 3:36de8b9e4b1a | 317 | |
darienf | 3:36de8b9e4b1a | 318 | FieldInfo endEventLoopFieldInfo = eventRunnerType.GetField( |
darienf | 3:36de8b9e4b1a | 319 | "endEventLoop", BindingFlags.Instance | BindingFlags.NonPublic); |
darienf | 3:36de8b9e4b1a | 320 | |
darienf | 3:36de8b9e4b1a | 321 | FieldInfo eventLoopEndedSignalFieldInfo = eventRunnerType.GetField( |
darienf | 3:36de8b9e4b1a | 322 | "eventLoopEndedSignal", BindingFlags.Instance | BindingFlags.NonPublic); |
darienf | 3:36de8b9e4b1a | 323 | |
darienf | 3:36de8b9e4b1a | 324 | FieldInfo waitCommEventWaitHandleFieldInfo = eventRunnerType.GetField( |
darienf | 3:36de8b9e4b1a | 325 | "waitCommEventWaitHandle", BindingFlags.Instance | BindingFlags.NonPublic); |
darienf | 3:36de8b9e4b1a | 326 | |
darienf | 3:36de8b9e4b1a | 327 | if (endEventLoopFieldInfo == null |
darienf | 3:36de8b9e4b1a | 328 | || eventLoopEndedSignalFieldInfo == null |
darienf | 3:36de8b9e4b1a | 329 | || waitCommEventWaitHandleFieldInfo == null) |
darienf | 3:36de8b9e4b1a | 330 | { |
darienf | 3:36de8b9e4b1a | 331 | s_Log.WarnFormat( |
darienf | 3:36de8b9e4b1a | 332 | "Unable to find the EventLoopRunner internal wait handle or loop signal fields. " |
darienf | 3:36de8b9e4b1a | 333 | + "SerialPort workaround failure. Application may crash after " |
darienf | 3:36de8b9e4b1a | 334 | + "disposing SerialPort unless .NET 1.1 unhandled exception " |
darienf | 3:36de8b9e4b1a | 335 | + "policy is enabled from the application's config file."); |
darienf | 3:36de8b9e4b1a | 336 | } |
darienf | 3:36de8b9e4b1a | 337 | else |
darienf | 3:36de8b9e4b1a | 338 | { |
darienf | 3:36de8b9e4b1a | 339 | s_Log.DebugFormat( |
darienf | 3:36de8b9e4b1a | 340 | "Waiting for the SerialPort internal EventLoopRunner thread to finish..."); |
darienf | 3:36de8b9e4b1a | 341 | |
darienf | 3:36de8b9e4b1a | 342 | var eventLoopEndedWaitHandle = |
darienf | 3:36de8b9e4b1a | 343 | (WaitHandle)eventLoopEndedSignalFieldInfo.GetValue(eventRunner); |
darienf | 3:36de8b9e4b1a | 344 | var waitCommEventWaitHandle = |
darienf | 3:36de8b9e4b1a | 345 | (ManualResetEvent)waitCommEventWaitHandleFieldInfo.GetValue(eventRunner); |
darienf | 3:36de8b9e4b1a | 346 | |
darienf | 3:36de8b9e4b1a | 347 | endEventLoopFieldInfo.SetValue(eventRunner, true); |
darienf | 3:36de8b9e4b1a | 348 | |
darienf | 3:36de8b9e4b1a | 349 | // Sometimes the event loop handler resets the wait handle |
darienf | 3:36de8b9e4b1a | 350 | // before exiting the loop and hangs (in case of USB disconnect) |
darienf | 3:36de8b9e4b1a | 351 | // In case it takes too long, brute-force it out of its wait by |
darienf | 3:36de8b9e4b1a | 352 | // setting the handle again. |
darienf | 3:36de8b9e4b1a | 353 | do |
darienf | 3:36de8b9e4b1a | 354 | { |
darienf | 3:36de8b9e4b1a | 355 | waitCommEventWaitHandle.Set(); |
darienf | 3:36de8b9e4b1a | 356 | } while (!eventLoopEndedWaitHandle.WaitOne(2000)); |
darienf | 3:36de8b9e4b1a | 357 | |
darienf | 3:36de8b9e4b1a | 358 | s_Log.DebugFormat("Wait completed. Now it is safe to continue disposal."); |
darienf | 3:36de8b9e4b1a | 359 | } |
darienf | 3:36de8b9e4b1a | 360 | } |
darienf | 3:36de8b9e4b1a | 361 | } |
darienf | 3:36de8b9e4b1a | 362 | catch (Exception ex) |
darienf | 3:36de8b9e4b1a | 363 | { |
darienf | 3:36de8b9e4b1a | 364 | s_Log.ErrorFormat( |
darienf | 3:36de8b9e4b1a | 365 | "SerialPort workaround failure. Application may crash after " |
darienf | 3:36de8b9e4b1a | 366 | + "disposing SerialPort unless .NET 1.1 unhandled exception " |
darienf | 3:36de8b9e4b1a | 367 | + "policy is enabled from the application's config file: {0}", |
darienf | 3:36de8b9e4b1a | 368 | ex); |
darienf | 3:36de8b9e4b1a | 369 | } |
darienf | 3:36de8b9e4b1a | 370 | } |
darienf | 3:36de8b9e4b1a | 371 | } |
darienf | 3:36de8b9e4b1a | 372 | } |