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