C# SerialPort Crashing App After Close

Greetings Experts! I am facing a dilemma and finding myself scratching the remaining few bits of hair off my head. I connect to a SerialPort to pull weight data from a scale (industrial size). The data is received correctly and all information is updated appropriatley. The scale remains connected for the life of the application until exit. Upon exit, I can verify the connection to the scale and the thread controlling the scale connection are closed. Immediatly after that, I receive an App Crash error from Windows. If I remove the scale connection, the window will close just fine. This is difficult to debug because I am having to test at my customers location remotely. I have attached the code for the scale thread and the exiting code (Form_Closing event) .
protected ManualResetEvent threadStop = new ManualResetEvent(false);

//Init the scale thread
            if (scaleThread == null)
            {
                scaleThread = new Thread(new ThreadStart(InitScaleThread));
                scaleThread.IsBackground = true;
                scaleThread.Start();
            }

private void InitScaleThread()
{
    
    // --Code removed, just updating buttons and labels and pulling data into DataSet
    
    if (CommonMembers.IsValidDataSet(oDS))
    {
	DataRow oDR = oDS.Tables[0].Rows[0];

	using (scalePort = new SerialPort(oDR["Port"].ToString()))
	{
	    try
	    {
		scalePort.Parity = (System.IO.Ports.Parity)Enum.Parse(typeof(System.IO.Ports.Parity), oDR["Parity"].ToString());
		scalePort.BaudRate = Convert.ToInt32(oDR["BaudRate"].ToString());
		scalePort.StopBits = (System.IO.Ports.StopBits)Enum.Parse(typeof(System.IO.Ports.StopBits), oDR["StopBits"].ToString());
		scalePort.DataBits = Convert.ToInt32(oDR["DataBits"].ToString());
		scalePort.Handshake = Handshake.XOnXOff;
		scalePort.ReadTimeout = 2000;

		scalePort.Open();

		string data, prevdata;
		prevdata = "";
		while (scalePort.IsOpen)
		{
		    data = scalePort.ReadLine();

		    if ((data.Length > 0) && prevdata != data)
		    {
			prevdata = data;
			scalePort_DataReceived(scalePort, data);
		    }

		    Thread.Sleep(10);

		    if (threadStop.WaitOne(0))
		    {
		    	// Added the below as a test, although it shouldn't hurt anything
			scalePort.DtrEnable = false;
			scalePort.RtsEnable = false;
			scalePort.Close();
			break;
		    }
		}
	    }
	    catch (TimeoutException)
	    {
		// Do Nothing
	    }
	    catch (InvalidOperationException)
	    {
	    	// Added to try and capture error if any for testing only
	    	CommonMembers.SendSupportEmail();		
	    }
	    catch (UnauthorizedAccessException)
	    {
	    	// Added to try and capture error if any for testing only
	    	CommonMembers.SendSupportEmail();
	    }
	    catch (IOException)
	    {
	    	// Added to try and capture error if any for testing only
		CommonMembers.SendSupportEmail();
	    }
	}     
    }
    else
    {
	// -- Removed Code updating buttons and label, etc...
    }

}

Open in new window

void scalePort_DataReceived(object sender, String DataRead)
{
    try
    {
	string value = "";
	float scaleValue = 0;

	char[] cData = DataRead.ToCharArray();

	// Break the data apart and find the numbers - Should probably be done with Regex
	for (int i = 0; i < cData.Length; i++)
	{
	    if (CommonMembers.IsNumeric(Convert.ToString(cData[i])))
		value += Convert.ToString(cData[i]);
	    else if (Convert.ToString(cData[i]) == "L" || Convert.ToString(cData[i]) == "T")
		break;
	    Thread.Sleep(1);
	}

	if (CommonMembers.IsFloat(value))
	{
	    scaleValue = float.Parse(value);
		
	    // Update PIN Location and gauge max values
	    if (scaleValue < 500)
		SetScaleMaxValue(500);
	    else if (scaleValue > arcScale.MaxValue)
		SetScaleMaxValue(scaleValue + 50);
	    else if (scaleValue < 0)
		SetScaleValue(0);
	    else
	    {
		SetScaleValue(scaleValue);
		SetScaleVisibleValue(scaleValue.ToString());
	    }
	}
    }
    catch { }
}

Open in new window

// In Form_Closing Event
stopThread.Set();

if (scaleThread != null)
                {
// Added for testing
                    scaleThread.Abort();
                    // Added for testing
                    while (scaleThread.ThreadState != ThreadState.Stopped)
                    {
                        Thread.Sleep(500);
                    }
                }
                scaleThread = null;

Open in new window

LVL 2
ThePlague1347Asked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Chuck YetterCommented:
Sounds like the serial port is still open in a background thread when the app closes.  Add code in your Form_Closing event to make sure the serial port is closed (see code).

You can also add a call to Application.ExitThread() in the Form_Closing event.  This should close all background threads which may still be running.
//Close and dispose the scale port.
            if (scalePort != null)
            {
                try
                {
                    if (scalePort.IsOpen == true)
                    {
                        scalePort.DiscardInBuffer();
                        scalePort.DiscardOutBuffer();
                        scalePort.Close();
                    }
                }
                catch (Exception ex)
                {
                }

                if (scalePort != null)
                {
                    scalePort.Dispose();
                }
            }

Open in new window

0
ThePlague1347Author Commented:
Thank you for your response Axshun! I tried your implementation, the application no longer crashes. Instead it is hanging now (freezing) so something is still holding onto the port waiting for it to close.

I also added Application.ExitThread(); to the Form_Closing. This is the Form_Closing on the Main form so I hope that is not duplicating Form_Closing calls on exit.
0
Mike TomlinsonHigh School Computer Science, Computer Applications, and Mathematics TeachersCommented:
Try creating a boolean flag that gets toggled when the form closes:

    private boolean ShuttingDown = false;

Toggle that flag in the FormClosing() event:

    // In Form_Closing Event
    ShuttingDown = true;

Now change your loop to check for this flag, and Close() the port after that loop exits:

                while (scalePort.IsOpen && !ShuttingDown)
                {
                    ...
                }
                try
                {
                    if (scalePort.IsOpen == true)
                    {
                        scalePort.DiscardInBuffer();
                        scalePort.DiscardOutBuffer();
                        scalePort.Close();
                    }
                }
                catch (Exception ex)
                {
                }

Note that calling Close() inherently also Disposes()!
http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.close.aspx

    "Calling this method closes the SerialPort object and clears both the receive and transmit buffers. This method calls the Component.Dispose() method, which invokes the protected SerialPort.Dispose(Boolean) method with the disposing parameter set to true."
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Python 3 Fundamentals

This course will teach participants about installing and configuring Python, syntax, importing, statements, types, strings, booleans, files, lists, tuples, comprehensions, functions, and classes.

ThePlague1347Author Commented:
Thank you for your feedback Idle_Mind. I implemented your suggestion and it still did not work.

You are correct on the Close() method. However, I was not clear on the version of .NET I was using.  In 3.5 the App went back to crashing. I recompiled to 4, the app still hangs on close.
0
Mike TomlinsonHigh School Computer Science, Computer Applications, and Mathematics TeachersCommented:
Do you still have the ManualResetEvent in there?...don't think that is necessary.
0
ThePlague1347Author Commented:
No I commented it out...it wouldn't have hurt cause we are checking for null on the scalePort but just to be sure I did.
0
ThePlague1347Author Commented:
It appears my background thread is not closing. After running a few other tests the port appears to be closing the application closing is not terminating the thread. I have tried scaleThread.Join() and scaleThread.Abort() (I know the Abort "usually" closes and is not good, but just for giggles). Any other suggestions?
0
Mike TomlinsonHigh School Computer Science, Computer Applications, and Mathematics TeachersCommented:
That's why I said you probably don't need the ManualResetEvent in there...

Do you really need to explicitly close the serial port anyways?  You've placed it into a "using" block which means it will automatically be disposed of.  Also, since you set IsBackground() to true, the thread should be killed automatically when the app exits.
0
ThePlague1347Author Commented:
ManualResetEvent is no longer used.
The port is closing...the thread is not.
The thread IsBackground is set to true...thats the confusing part. Application.ExitThread waits for all threads to close right?...the AppCrash happens shortly after joing the thread (it appears).
0
Mike TomlinsonHigh School Computer Science, Computer Applications, and Mathematics TeachersCommented:
Where and why are you Join()ing which thread on exit?...that should not be necessary either.
0
ThePlague1347Author Commented:
I'm trying to test different stuff just to get it to work, LOL
0
ThePlague1347Author Commented:
Joining the thread should add it to the applications main thread so it doesn't have to wait for it. I even added a 5 second delay to allow the thread to close. No beans. Gotta double check that though, I'm using 4.0 now. I can check ThreadState which hangs the app.
0
Mike TomlinsonHigh School Computer Science, Computer Applications, and Mathematics TeachersCommented:
Could you explain your first statement more...it doesn't make sense to me.

0
ThePlague1347Author Commented:
From my understanding ( and please correct me as I am pulling from the top of my head ). The Thread.Join should add the background thread to main thread (the application). When the application closes, it no longer waiting for that backgroung thread to close because it is now a part of the main thread.

On a side note...

I added a while loop to check for ThreadState. The ThreadState.Aborted is shown is and the Form_Closing continues....with a crash. Cameras, etc are closed first...e.cancel = false; is the last/next line to be called. If I "erase" all scale connections the applications closes like it should. This is driving me nuts.
0
Mike TomlinsonHigh School Computer Science, Computer Applications, and Mathematics TeachersCommented:
The Join() method tells the current thread to STOP until the thread being joined has completed:
http://msdn.microsoft.com/en-us/library/95hbf2ta.aspx

    "Blocks the calling thread until a thread terminates, while continuing to perform standard COM and SendMessage pumping."

For example, assume the current thread is "A", and we Join() with thread "B":

    // ... currently running in threadA ...
    threadB.Join(); // <-- threadA STOPS here until threadB EXITS
    // ... threadA continues running ...

So this could theoretically keep your application from exiting if threadB never exits properly.  You could be ending up in a race condition where the main thread is waiting for the secondary thread to exit but the secondary thread cannot exit because some condition or ManualResetEvent has not been set and/or processed (since the main app message pump is NOT processing while Join()ed).
0
ThePlague1347Author Commented:
So, Thread.Join(), we can rule out. Thread "B" has exited and thread "A" (main thread) crashes. (Again, ManualResetEvent is not being used, deleted/erased.  It was doing that before I added the ManualResetEvent).
0
ThePlague1347Author Commented:
Alright, well. After reviewing the crashdump logs, it turns out after all this it was the DVR camera system not shutting down correctly. Although I think we did resolve some issues with the scale connection(s) at the same time. Thanks for all your help!
0
Mike TomlinsonHigh School Computer Science, Computer Applications, and Mathematics TeachersCommented:
Glad you figured it out!...as given the root cause, we probably never would have guessed that from our end.  =)
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
.NET Programming

From novice to tech pro — start learning today.