2017-12-29 14:54:43 -08:00
// Copyright 2010-2014 Zach Saw
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using Microsoft.Win32.SafeHandles ;
using System ;
using System.IO ;
using System.Runtime.InteropServices ;
using System.Text ;
namespace MatterHackers.SerialPortCommunication.FrostedSerial
{
2019-02-12 13:25:35 -08:00
/// <summary>
/// SerialPort helper intended to fix IOExceptions in stock Windows implementation, as described in http://zachsaw.blogspot.com/2010/07/net-serialport-woes.html
/// </summary>
2017-12-29 14:54:43 -08:00
public class SerialPortFixer : IDisposable
{
public static void Execute ( string portName )
{
using ( new SerialPortFixer ( portName ) )
{
}
}
#region IDisposable Members
public void Dispose ( )
{
if ( m_Handle ! = null )
{
m_Handle . Close ( ) ;
m_Handle = null ;
}
}
#endregion IDisposable Members
#region Implementation
private const int DcbFlagAbortOnError = 14 ;
private const int CommStateRetries = 10 ;
private SafeFileHandle m_Handle ;
private SerialPortFixer ( string portName )
{
const int dwFlagsAndAttributes = 0x40000000 ;
const int dwAccess = unchecked ( ( int ) 0xC0000000 ) ;
if ( ( portName = = null ) | | ! portName . StartsWith ( "COM" , StringComparison . OrdinalIgnoreCase ) )
{
throw new ArgumentException ( "Invalid Serial Port" , "portName" ) ;
}
SafeFileHandle hFile = CreateFile ( @"\\.\" + portName , dwAccess , 0 , IntPtr . Zero , 3 , dwFlagsAndAttributes , IntPtr . Zero ) ;
if ( hFile . IsInvalid )
{
WinIoError ( ) ;
}
try
{
int fileType = GetFileType ( hFile ) ;
if ( ( fileType ! = 2 ) & & ( fileType ! = 0 ) )
{
throw new ArgumentException ( "Invalid Serial Port" , "portName" ) ;
}
m_Handle = hFile ;
InitializeDcb ( ) ;
}
catch
{
hFile . Close ( ) ;
m_Handle = null ;
throw ;
}
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int FormatMessage ( int dwFlags , HandleRef lpSource , int dwMessageId , int dwLanguageId ,
StringBuilder lpBuffer , int nSize , IntPtr arguments ) ;
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool GetCommState ( SafeFileHandle hFile , ref Dcb lpDcb ) ;
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool SetCommState ( SafeFileHandle hFile , ref Dcb lpDcb ) ;
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool ClearCommError ( SafeFileHandle hFile , ref int lpErrors , ref Comstat lpStat ) ;
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern SafeFileHandle CreateFile ( string lpFileName , int dwDesiredAccess , int dwShareMode ,
IntPtr securityAttrs , int dwCreationDisposition ,
int dwFlagsAndAttributes , IntPtr hTemplateFile ) ;
[DllImport("kernel32.dll", SetLastError = true)]
private static extern int GetFileType ( SafeFileHandle hFile ) ;
private void InitializeDcb ( )
{
Dcb dcb = new Dcb ( ) ;
GetCommStateNative ( ref dcb ) ;
dcb . Flags & = ~ ( 1 u < < DcbFlagAbortOnError ) ;
SetCommStateNative ( ref dcb ) ;
}
private static string GetMessage ( int errorCode )
{
StringBuilder lpBuffer = new StringBuilder ( 0x200 ) ;
if (
FormatMessage ( 0x3200 , new HandleRef ( null , IntPtr . Zero ) , errorCode , 0 , lpBuffer , lpBuffer . Capacity ,
IntPtr . Zero ) ! = 0 )
{
return lpBuffer . ToString ( ) ;
}
return "Unknown Error" ;
}
private static int MakeHrFromErrorCode ( int errorCode )
{
return ( int ) ( 0x80070000 | ( uint ) errorCode ) ;
}
private static void WinIoError ( )
{
int errorCode = Marshal . GetLastWin32Error ( ) ;
throw new IOException ( GetMessage ( errorCode ) , MakeHrFromErrorCode ( errorCode ) ) ;
}
private void GetCommStateNative ( ref Dcb lpDcb )
{
int commErrors = 0 ;
Comstat comStat = new Comstat ( ) ;
for ( int i = 0 ; i < CommStateRetries ; i + + )
{
if ( ! ClearCommError ( m_Handle , ref commErrors , ref comStat ) )
{
WinIoError ( ) ;
}
if ( GetCommState ( m_Handle , ref lpDcb ) )
{
break ;
}
if ( i = = CommStateRetries - 1 )
{
WinIoError ( ) ;
}
}
}
private void SetCommStateNative ( ref Dcb lpDcb )
{
int commErrors = 0 ;
Comstat comStat = new Comstat ( ) ;
for ( int i = 0 ; i < CommStateRetries ; i + + )
{
if ( ! ClearCommError ( m_Handle , ref commErrors , ref comStat ) )
{
WinIoError ( ) ;
}
if ( SetCommState ( m_Handle , ref lpDcb ) )
{
break ;
}
if ( i = = CommStateRetries - 1 )
{
WinIoError ( ) ;
}
}
}
#region Nested type : COMSTAT
[StructLayout(LayoutKind.Sequential)]
private struct Comstat
{
public readonly uint Flags ;
public readonly uint cbInQue ;
public readonly uint cbOutQue ;
}
#endregion Nested type : COMSTAT
#region Nested type : DCB
[StructLayout(LayoutKind.Sequential)]
private struct Dcb
{
public readonly uint DCBlength ;
public readonly uint BaudRate ;
public uint Flags ;
public readonly ushort wReserved ;
public readonly ushort XonLim ;
public readonly ushort XoffLim ;
public readonly byte ByteSize ;
public readonly byte Parity ;
public readonly byte StopBits ;
public readonly byte XonChar ;
public readonly byte XoffChar ;
public readonly byte ErrorChar ;
public readonly byte EofChar ;
public readonly byte EvtChar ;
public readonly ushort wReserved1 ;
}
#endregion Nested type : DCB
#endregion Implementation
}
}