2 // System.IO.Ports.WinSerialStream.cs
5 // Carlos Alberto Cortez (calberto.cortez@gmail.com)
7 // (c) Copyright 2006 Novell, Inc. (http://www.novell.com)
13 using System.Runtime.InteropServices;
14 using System.Threading;
15 using System.ComponentModel;
19 namespace System.IO.Ports
21 class WinSerialStream : Stream, ISerialStream, IDisposable
23 // Windows API Constants
24 const uint GenericRead = 0x80000000;
25 const uint GenericWrite = 0x40000000;
26 const uint OpenExisting = 3;
27 const uint FileFlagOverlapped = 0x40000000;
28 const uint PurgeRxClear = 0x0004;
29 const uint PurgeTxClear = 0x0008;
30 const uint WinInfiniteTimeout = 0xFFFFFFFF;
31 const uint FileIOPending = 997;
34 const uint SetRts = 3;
35 const uint ClearRts = 4;
36 const uint SetDtr = 5;
37 const uint ClearDtr = 6;
38 const uint SetBreak = 8;
39 const uint ClearBreak = 9;
40 const uint CtsOn = 0x0010;
41 const uint DsrOn = 0x0020;
42 const uint RsldOn = 0x0080;
45 const uint EvRxChar = 0x0001;
46 const uint EvCts = 0x0008;
47 const uint EvDsr = 0x0010;
48 const uint EvRlsd = 0x0020;
49 const uint EvBreak = 0x0040;
50 const uint EvErr = 0x0080;
51 const uint EvRing = 0x0100;
57 IntPtr write_overlapped;
58 IntPtr read_overlapped;
59 ManualResetEvent read_event;
60 ManualResetEvent write_event;
63 [DllImport("kernel32", SetLastError = true)]
64 static extern int CreateFile(string port_name, uint desired_access,
65 uint share_mode, uint security_attrs, uint creation, uint flags,
68 [DllImport("kernel32", SetLastError = true)]
69 static extern bool SetupComm(int handle, int read_buffer_size, int write_buffer_size);
71 [DllImport("kernel32", SetLastError = true)]
72 static extern bool PurgeComm(int handle, uint flags);
74 [DllImport("kernel32", SetLastError = true)]
75 static extern bool SetCommTimeouts(int handle, Timeouts timeouts);
77 public WinSerialStream (string port_name, int baud_rate, int data_bits, Parity parity, StopBits sb,
78 bool dtr_enable, bool rts_enable, Handshake hs, int read_timeout, int write_timeout,
79 int read_buffer_size, int write_buffer_size)
81 handle = CreateFile (port_name != null && !port_name.StartsWith(@"\\.\")
82 ? @"\\.\" + port_name : port_name,
83 GenericRead | GenericWrite, 0, 0, OpenExisting,
84 FileFlagOverlapped, 0);
87 ReportIOError (port_name);
89 // Set port low level attributes
90 SetAttributes (baud_rate, parity, data_bits, sb, hs);
92 // Clean buffers and set sizes
93 if (!PurgeComm (handle, PurgeRxClear | PurgeTxClear) ||
94 !SetupComm (handle, read_buffer_size, write_buffer_size))
98 this.read_timeout = read_timeout;
99 this.write_timeout = write_timeout;
100 timeouts = new Timeouts (read_timeout, write_timeout);
101 if (!SetCommTimeouts(handle, timeouts))
102 ReportIOError (null);
105 SetSignal(SerialSignal.Dtr, dtr_enable);
107 if (hs != Handshake.RequestToSend &&
108 hs != Handshake.RequestToSendXOnXOff)
109 SetSignal(SerialSignal.Rts, rts_enable);
111 // Init overlapped structures
112 NativeOverlapped wo = new NativeOverlapped ();
113 write_event = new ManualResetEvent (false);
115 wo.EventHandle = write_event.Handle;
117 wo.EventHandle = (int) write_event.Handle;
119 write_overlapped = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (NativeOverlapped)));
120 Marshal.StructureToPtr (wo, write_overlapped, true);
122 NativeOverlapped ro = new NativeOverlapped ();
123 read_event = new ManualResetEvent (false);
125 ro.EventHandle = read_event.Handle;
127 ro.EventHandle = (int) read_event.Handle;
129 read_overlapped = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (NativeOverlapped)));
130 Marshal.StructureToPtr (ro, read_overlapped, true);
133 public override bool CanRead {
139 public override bool CanSeek {
145 public override bool CanTimeout {
151 public override bool CanWrite {
157 public override int ReadTimeout {
162 if (value < 0 && value != SerialPort.InfiniteTimeout)
163 throw new ArgumentOutOfRangeException ("value");
165 timeouts.SetValues (value, write_timeout);
166 if (!SetCommTimeouts (handle, timeouts))
167 ReportIOError (null);
169 read_timeout = value;
173 public override int WriteTimeout {
175 return write_timeout;
179 if (value < 0 && value != SerialPort.InfiniteTimeout)
180 throw new ArgumentOutOfRangeException ("value");
182 timeouts.SetValues (read_timeout, value);
183 if (!SetCommTimeouts (handle, timeouts))
184 ReportIOError (null);
186 write_timeout = value;
190 public override long Length {
192 throw new NotSupportedException ();
196 public override long Position {
198 throw new NotSupportedException ();
201 throw new NotSupportedException ();
205 [DllImport("kernel32", SetLastError = true)]
206 static extern bool CloseHandle (int handle);
208 protected override void Dispose (bool disposing)
214 CloseHandle (handle);
215 Marshal.FreeHGlobal (write_overlapped);
216 Marshal.FreeHGlobal (read_overlapped);
219 void IDisposable.Dispose ()
222 GC.SuppressFinalize (this);
225 public override void Close ()
227 ((IDisposable)this).Dispose ();
235 public override void Flush ()
241 public override long Seek (long offset, SeekOrigin origin)
243 throw new NotSupportedException();
246 public override void SetLength (long value)
248 throw new NotSupportedException();
252 [DllImport("kernel32", SetLastError = true)]
253 static extern unsafe bool ReadFile (int handle, byte* buffer, int bytes_to_read,
254 out int bytes_read, IntPtr overlapped);
256 [DllImport("kernel32", SetLastError = true)]
257 static extern unsafe bool GetOverlappedResult (int handle, IntPtr overlapped,
258 ref int bytes_transfered, bool wait);
261 public override int Read ([In, Out] byte [] buffer, int offset, int count)
265 throw new ArgumentNullException ("buffer");
266 if (offset < 0 || count < 0)
267 throw new ArgumentOutOfRangeException ("offset or count less than zero.");
269 if (buffer.Length - offset < count )
270 throw new ArgumentException ("offset+count",
271 "The size of the buffer is less than offset + count.");
276 fixed (byte* ptr = buffer) {
277 if (ReadFile (handle, ptr + offset, count, out bytes_read, read_overlapped))
280 // Test for overlapped behavior
281 if (Marshal.GetLastWin32Error () != FileIOPending)
282 ReportIOError (null);
284 if (!GetOverlappedResult (handle, read_overlapped, ref bytes_read, true))
285 ReportIOError (null);
291 throw new TimeoutException (); // We didn't get any byte
297 [DllImport("kernel32", SetLastError = true)]
298 static extern unsafe bool WriteFile (int handle, byte* buffer, int bytes_to_write,
299 out int bytes_written, IntPtr overlapped);
302 public override void Write (byte [] buffer, int offset, int count)
306 throw new ArgumentNullException ("buffer");
308 if (offset < 0 || count < 0)
309 throw new ArgumentOutOfRangeException ();
311 if (buffer.Length - offset < count)
312 throw new ArgumentException ("offset+count",
313 "The size of the buffer is less than offset + count.");
315 int bytes_written = 0;
318 fixed (byte* ptr = buffer) {
319 if (WriteFile (handle, ptr + offset, count, out bytes_written, write_overlapped))
321 if (Marshal.GetLastWin32Error() != FileIOPending)
322 ReportIOError (null);
324 if (!GetOverlappedResult(handle, write_overlapped, ref bytes_written, true))
325 ReportIOError (null);
329 // If the operation timed out, then
330 // we transfered less bytes than the requested ones
331 if (bytes_written < count)
332 throw new TimeoutException ();
335 [DllImport("kernel32", SetLastError = true)]
336 static extern bool GetCommState (int handle, [Out] DCB dcb);
338 [DllImport ("kernel32", SetLastError=true)]
339 static extern bool SetCommState (int handle, DCB dcb);
341 public void SetAttributes (int baud_rate, Parity parity, int data_bits, StopBits bits, Handshake hs)
343 DCB dcb = new DCB ();
344 if (!GetCommState (handle, dcb))
345 ReportIOError (null);
347 dcb.SetValues (baud_rate, parity, data_bits, bits, hs);
348 if (!SetCommState (handle, dcb))
349 ReportIOError (null);
352 void ReportIOError(string optional_arg)
354 int error = Marshal.GetLastWin32Error ();
359 message = "The port `" + optional_arg + "' does not exist.";
362 message = "Parameter is incorrect.";
365 // As fallback, we show the win32 error
366 message = new Win32Exception ().Message;
370 throw new IOException (message);
373 void CheckDisposed ()
376 throw new ObjectDisposedException (GetType ().FullName);
379 // ISerialStream members
380 public void DiscardInBuffer ()
382 if (!PurgeComm (handle, PurgeRxClear))
383 ReportIOError (null);
386 public void DiscardOutBuffer ()
388 if (!PurgeComm (handle, PurgeRxClear))
389 ReportIOError (null);
392 [DllImport ("kernel32", SetLastError=true)]
393 static extern bool ClearCommError (int handle, out uint errors, out CommStat stat);
395 public int BytesToRead {
399 if (!ClearCommError (handle, out errors, out stat))
400 ReportIOError (null);
402 return (int)stat.BytesIn;
406 public int BytesToWrite {
410 if (!ClearCommError (handle, out errors, out stat))
411 ReportIOError (null);
413 return (int)stat.BytesOut;
417 [DllImport ("kernel32", SetLastError=true)]
418 static extern bool GetCommModemStatus (int handle, out uint flags);
420 public SerialSignal GetSignals ()
423 if (!GetCommModemStatus (handle, out flags))
424 ReportIOError (null);
426 SerialSignal signals = SerialSignal.None;
427 if ((flags & RsldOn) != 0)
428 signals |= SerialSignal.Cd;
429 if ((flags & CtsOn) != 0)
430 signals |= SerialSignal.Cts;
431 if ((flags & DsrOn) != 0)
432 signals |= SerialSignal.Dsr;
437 [DllImport ("kernel32", SetLastError=true)]
438 static extern bool EscapeCommFunction (int handle, uint flags);
440 public void SetSignal (SerialSignal signal, bool value)
442 if (signal != SerialSignal.Rts && signal != SerialSignal.Dtr)
443 throw new Exception ("Wrong internal value");
446 if (signal == SerialSignal.Rts)
457 if (!EscapeCommFunction (handle, flag))
458 ReportIOError (null);
461 public void SetBreakState (bool value)
463 if (!EscapeCommFunction (handle, value ? SetBreak : ClearBreak))
464 ReportIOError (null);
469 [StructLayout (LayoutKind.Sequential)]
472 public int dcb_length;
473 public int baud_rate;
475 public short w_reserved;
476 public short xon_lim;
477 public short xoff_lim;
478 public byte byte_size;
480 public byte stop_bits;
481 public byte xon_char;
482 public byte xoff_char;
483 public byte error_char;
484 public byte eof_char;
485 public byte evt_char;
486 public short w_reserved1;
489 //const int fBinary = 0x0001;
490 //const int fParity = 0x0002;
491 const int fOutxCtsFlow = 0x0004;
492 //const int fOutxDsrFlow1 = 0x0008;
493 //const int fOutxDsrFlow2 = 0x0010;
494 //const int fDtrControl = 0x00020;
495 //const int fDsrSensitivity = 0x0040;
496 //const int fTXContinueOnXoff = 0x0080;
497 const int fOutX = 0x0100;
498 const int fInX = 0x0200;
499 //const int fErrorChar = 0x0400;
500 //const int fNull = 0x0800;
501 //const int fRtsControl1 = 0x1000;
502 const int fRtsControl2 = 0x2000;
503 //const int fAbortOnError = 0x4000;
505 public void SetValues (int baud_rate, Parity parity, int byte_size, StopBits sb, Handshake hs)
511 case StopBits.OnePointFive:
517 default: // Shouldn't happen
521 this.baud_rate = baud_rate;
522 this.parity = (byte)parity;
523 this.byte_size = (byte)byte_size;
525 // Clear Handshake flags
526 flags &= ~(fOutxCtsFlow | fOutX | fInX | fRtsControl2);
528 // Set Handshake flags
533 case Handshake.XOnXOff:
534 flags |= fOutX | fInX;
536 case Handshake.RequestToSend:
537 flags |= fOutxCtsFlow | fRtsControl2;
539 case Handshake.RequestToSendXOnXOff:
540 flags |= fOutxCtsFlow | fOutX | fInX | fRtsControl2;
542 default: // Shouldn't happen
548 [StructLayout (LayoutKind.Sequential)]
551 public uint ReadIntervalTimeout;
552 public uint ReadTotalTimeoutMultiplier;
553 public uint ReadTotalTimeoutConstant;
554 public uint WriteTotalTimeoutMultiplier;
555 public uint WriteTotalTimeoutConstant;
557 public const uint MaxDWord = 0xFFFFFFFF;
559 public Timeouts (int read_timeout, int write_timeout)
561 SetValues (read_timeout, write_timeout);
564 public void SetValues (int read_timeout, int write_timeout)
566 // FIXME: The windows api docs are not very clear about read timeouts,
567 // and we have to simulate infinite with a big value (uint.MaxValue - 1)
568 ReadIntervalTimeout = MaxDWord;
569 ReadTotalTimeoutMultiplier = MaxDWord;
570 ReadTotalTimeoutConstant = (read_timeout == -1 ? MaxDWord - 1 : (uint) read_timeout);
572 WriteTotalTimeoutMultiplier = 0;
573 WriteTotalTimeoutConstant = (write_timeout == -1 ? MaxDWord : (uint) write_timeout);
578 [StructLayout (LayoutKind.Sequential)]
583 public uint BytesOut;