Search

SerialPort.Close() hangs the application by Vince Noga

Closed
as By Design Help for as By Design

2
0
Sign in
to vote
Type: Bug
ID: 202137
Opened: 9/13/2006 12:11:37 PM
Access Restriction: Public
3
Workaround(s)
3
User(s) can reproduce this bug
Calling SerialPort.Close() on an open serial port hangs the application.
Details (expand)
Product Language
English
Version
Visual Studio 2005 Professional Edition
Operating System
Windows XP Professional
Operating System Language
English
Category
\.NET Framework
Subcategory 1
\Microsoft.CSharp
Subcategory 2
 
Subcategory 3
 
Steps to Reproduce
1) Create a new Windows Desktop application
2) Add 2 Buttons, a TextBox, and a SerialPort to the application.
3) Code the following event handlers:

private void button1_Click(object sender, EventArgs e)
{
if (!serialPort1.IsOpen)
{
try
{
serialPort1.PortName = "COM1";
serialPort1.BaudRate = 9600;
serialPort1.DataBits = 7;
serialPort1.Parity = System.IO.Ports.Parity.Even;
serialPort1.StopBits = System.IO.Ports.StopBits.One;
serialPort1.Open();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
}

private void button2_Click(object sender, EventArgs e)
{
if (serialPort1.IsOpen)
{
serialPort1.Close();
}
}

//Declare a method signature that can be Invoked
private delegate void UIUpdater(string s);

//A method that has the same signature as UIUpdater()
private void UIUpdate(string s)
{
textBox1.Text = s;
}

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
this.Invoke(new UIUpdater(UIUpdate),
new object[] { serialPort1.ReadExisting() });
}

4) Attach any device to COM1 (I used a scale) that continuously sends data

5) Compile and run the application

6) Open and close the serial port via buttons 1 and 2, respectively. Although the application may or may not hang on the first or even second open/close sequence, at some point, closing the serial port will hang the application. (In other words, if the application does not hang the first time the port is closed, try again. The application will hang.)
Actual Results
Application randomly hangs when the serial port is closed. Sometimes a single open/close hangs the application. Sometimes, you have to repeat the open/close.
Expected Results
I expect the serial port to close without hanging the application. My end-users do need to be able to stop receiving input.
File Attachments
0 attachments
Sign in to post a comment.
Posted by markoni on 8/1/2009 at 1:56 AM
Change "this.Invoke" in to "this.BeginInvoke".
Posted by Microsoft on 5/11/2007 at 5:09 PM
Thanks Vnoga and Nobugz, and we apologize for failing to post a workaround earlier!

As described in the blog Nobugz linked to, there is deadlock between the UI thread and the threadpool threads. So the workaround you described -- closing the port on another thread -- will work, but you can also replace Control.Invoke calls with Control.InvokeLater calls. This workaround is a less invasive and a good practice generally.

Thanks,
Kim
Posted by nobugz on 5/11/2007 at 2:19 PM
Kim Hamilton has posted a diagnostic and a workaround for this problem on this blog post:

http://blogs.msdn.com/bclteam/archive/2006/10/10/Top-5-SerialPort-Tips-_5B00_Kim-Hamilton_5D00_.aspx
Posted by Vince Noga on 9/19/2006 at 5:45 AM
Closing the port in another thread is the way to go.
Posted by Vince Noga on 9/18/2006 at 12:24 PM
Well, from futher testing, the workaround I posted today (09/18/2006) doesn't work 100% of the time. It made the hang less likely, but it can still happen.
Sign in to post a workaround.
Posted by Personal Information Withheld on 9/15/2006 at 7:32 AM
A (very lame) workaround is to do the close in a new thread. I haven't tested trying to reopen the port and I'm not sure that it would be a good thing to do!
Posted by Vince Noga on 9/18/2006 at 5:15 AM
Another (very lame) workaround is to update the user-interface (UI) in a different thread. It should not be this difficult to collect data from a serial port and display it in the UI.

*** WARNING ***
This simple example is just that, a simple example. It does not prevent data loss from the serial port to the UI.
***************

        private AutoResetEvent are = new AutoResetEvent(false);
        private bool portOpen = false;
        private string serialData = "";

        private void button1_Click(object sender, EventArgs e)
        {
            if (!serialPort1.IsOpen)
            {
                try
                {
                    serialPort1.PortName = "COM1";
                    serialPort1.BaudRate = 9600;
                    serialPort1.DataBits = 7;
                    serialPort1.Parity = System.IO.Ports.Parity.Even;
                    serialPort1.StopBits = System.IO.Ports.StopBits.One;
                    serialPort1.Open();
                    portOpen = true;
                    Thread t = new Thread(new ThreadStart(MonitorPort));
                    t.IsBackground = true;
                    t.Start();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK,
                        MessageBoxIcon.Error);
                }
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (serialPort1.IsOpen)
            {
                //Tell MonitorPort() to stop updating the UI
                portOpen = false;

                //Wait for MonitorPort() to signal that it's done
                are.WaitOne();

                serialPort1.Close();
            }
        }

        //Declare a method signature that can be Invoked
//        private delegate void UIUpdater(string s);
        private delegate void UIUpdater();

        //A method that has the same signature as UIUpdater()
//        private void UIUpdate(string s)
        private void UIUpdate()
        {
            textBox1.Text = serialData;
        }

        private void MonitorPort()
        {
            try
            {
                //All this thread does is write the serial data to the UI.
                //This simple example is not written to prevent data loss!
                while (portOpen)
                {
                    this.Invoke(new UIUpdater(UIUpdate));
                    Thread.Sleep(100);
                }
            }
            finally
            {
                //Signal that the thread is terminating
                are.Set();
            }
        }

        private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            //If continuous data from the device, application can hang when
         //SerialPort.Close() is called.
            //this.Invoke(new UIUpdater(UIUpdate),
            // new object[] { serialPort1.ReadExisting() });

            //Save the data to an object where .Invoke() is not required.
            //This simple example is not written to prevent data loss!
            serialData = serialPort1.ReadExisting();
        }
Posted by Unregistered User on 5/7/2007 at 3:42 PM
I'm confused. The Issue Status says "Closed (By Design)". If SerialPort->Close() is "Designed" to work this way then it should be renamed to:

SerialPort->HangApp(Intermittency=>50%).

It would have saved me days of debugging if it were named more appropiately.

Seriously, I know MS doesn't want to support "Legacy" serial devices (or anything not PnP) and wishes these devices would just go away, but there are a gazillion serial port devices still out there and manufacturers are still making new Serial Port based products which programmers still have to make apps for.

Unless MS can talk H/W Manufacturers out of making Serial devices, you gotta help us out.

So how do we get this Issue reopened?