Share via


Correctly releasing Com Port on exit

Question

Wednesday, October 30, 2019 3:37 PM

I am developing a C# program that communicates with positioners via RS232. I would like this program to let go of all serial ports when it shuts down and when it crashes. It doesn't really do either. I believe I have done everything recommended to correctly handle these com ports.

The actual architecture is USB to RS485 Converter. Every time I close and reopen the program, it can't get control of the ports and errors. I have to switch the USB cables to new jacks to free them, specifically:

`System.UnauthorizedAccessException: 'Access Denied: COM6'`

This happens 100% of the time if the program crashes or if I hit the square to stop debugging.
It happens 50% - 75% of the time if I shut down the debugging program orderly-like.

I do wonder if this is related to the debugging environment.

To try to fix this, I create an instance of the ComPort class for each com port that exists. It implements IDisposable and is supposed to release its com port on dispose.

Dispose() does fire as expected.

  

  public class ComPort : INotifyPropertyChanged, IDisposable
     {
      public ComPort(string name)
      {
       portStream.PortName = name;
       PositionerNodes.CollectionChanged += Nodes_CollectionChanged;
       SetPortDefaults();
    
       try
       {
        portStream.Open();
        // TODO: Implement user-select on number of nodes
       }
    
       catch (UnauthorizedAccessException ex)
       {
        if (name == "COM1")
        {
         // eat the exception
        }
        else
        {
         Reporter.Warn($"Unable to get access to {name}. Another process may be using it.", $"Exception:\n\n{ex.Message}\n\n\nInner exception:\n\n {ex.InnerException?.Message ?? "No inner"}");
    
         throw;
        }
       }
       <snip></snip>
      }
      <snip></snip>
     
      // IDisposable Support
      private bool disposedValue = false; // To detect redundant calls
    
      protected virtual void Dispose(bool disposing)
      {
       if (!disposedValue)
       {
        if (disposing)
        {
         foreach (Node node in Nodes)
         {
          node.Stop();
         }
        }
    
        // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
        Debug.WriteLine($"Start dispose {portStream.PortName}");
    #if DEBUG
        string report = $"Finish dispose {portStream.PortName}";
    #endif
        if (!portStream.IsDisposed)
        {
         if (portStream.IsOpen)
         {
          portStream.Close();
         }
         portStream.Dispose();
        }
        Debug.WriteLine(report);
    
        // TODO: set large fields to null.
        disposedValue = true;
       }
      }
    
      // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
      ~ComPort()
      {
       // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
       Dispose(false);
      }
    
      // This code added to correctly implement the disposable pattern.
      public void Dispose()
      {
       // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
       Dispose(true);
       // TODO: uncomment the following line if the finalizer is overridden above.
       GC.SuppressFinalize(this);
      }
    
     }

Also, I have a method in App.xaml.cs that is supposed to shut the comports down on Exit and on unhandled exceptions. These methods seem to be firing as expected.

  

public partial class App : Application
 {
  private void Application_Startup(object sender, StartupEventArgs e)
  {
   AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
  }
  private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
  {
   ShutDown();
  }
  private void Application_Exit(object sender, ExitEventArgs e)
  {
   ShutDown();
  }
  private void ShutDown()
  {
   foreach (Node node in ComPortManager.ComPorts.SelectMany(x => x.Nodes))
   {
    Debug.WriteLine($"Node: {node.ID}");
    node.Stop();
   }
   foreach (ComPort comPort in ComPortManager.ComPorts)
   {
    comPort.Dispose();
   }
  }
 }
}

All replies (5)

Thursday, October 31, 2019 3:07 AM | 1 vote

Hi Major_Major, 

Thank you for posting here.

According to your question, you find that your com port is not correctly released on exit.

Since ‘ComPort’ implements IDisposable interface, we can use using statement to release it.

Here are references you can refer to.

Freeing up Serial Port properly

How to Forcefully close SerialPort Connection?

Hope them can help you.

Best Regards,

Xingyu Zhao

MSDN Community Support
Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact [email protected].


Sunday, November 3, 2019 4:52 AM | 1 vote

There must be something else involved here.  ALL file handles are closed by the operating system when an application exits, either normally or abnormally.  Unless the application is not actually exiting, there cannot be any dangling opens when your app starts again.

Tim Roberts | Driver MVP Emeritus | Providenza & Boekelheide, Inc.


Sunday, November 3, 2019 5:02 AM

@ Xingyu Zhao

Thank you for posting. I believe that the code I posted does all of the things recommended in your links. 


Sunday, November 3, 2019 5:10 AM

@Tim Roberts

I think you are right, and I think the something else is the VS debug environment.

I spent about ten minutes running the debug build outside of VS and crashing it in every way I could come up with and never had the problem. I also went through two or three serial terminal emulators on GitHub and they weren't really taking any of the precautions I'm taking. 

It's a mild bummer, since it makes "The F5 experience" as one VS release notes writer called it, really terrible. But on the bright side, I'll never be able to fix it so I won't spend any more time on it.


Thursday, March 26, 2020 11:07 PM | 1 vote

In case someone else comes looking for this. The problem was that the xaml design time components were getting control of the com ports. On window open the program goes out and scans ports for serial peripherals. The design time components were running this initializer. Just had to close the xaml file.