C# Keep Serial Port Open After OnStart in Windows Service

I do not understand how to keep the Serial Port open for monitoring and recording of serial port data received after the OnStart of my Windows Service.  What am I missing or not understanding?  I thought that once I called the Open method of my Serial Port class it would stay open and just monitor.  It appears that after the OnStart the program ends even though it states running in Services window.  Below I have posted the class of my service.  Below that, I have posted my Serial Port class for completeness.

I know I have the correct port and settings because I can transmit successfully from the Windows 10 hyperterminal to the Windows Server 2012 R2 hyperterminal instance.  Of course, I close the server hyperterminal before I start my service since there are not conflicts.  Please help.  Thanks.

SERVICE PARTIAL CLASS
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;



namespace APPSecurityDoor
{
    public partial class APPSecurityDoorServ : ServiceBase
    {
        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern bool SetServiceStatus(IntPtr handle, ref ServiceStatus serviceStatus);
        private SerialPortActions serialPortActions = new SerialPortActions();


        public APPSecurityDoorServ()
        {
            InitializeComponent();
            eventLog1 = new System.Diagnostics.EventLog();
            if (!System.Diagnostics.EventLog.SourceExists("APPSecurityDoorSource"))
            {
                System.Diagnostics.EventLog.CreateEventSource("APPSecurityDoorSource", "APPSecurityDoorLog");
            }
            eventLog1.Source = "APPSecurityDoorSource";
            eventLog1.Log = "APPSecurityDoorLog";
            
        }

        protected override void OnStart(string[] args)
        {
            eventLog1.WriteEntry("In OnStart");

            // Update the service state to Start Pending.
            ServiceStatus serviceStatus = new ServiceStatus();
            serviceStatus.dwCurrentState = ServiceState.SERVICE_START_PENDING;
            serviceStatus.dwWaitHint = 100000;
            SetServiceStatus(this.ServiceHandle, ref serviceStatus);

            try
            {
                
                serialPortActions.Open();
            }
            catch (Exception e)
            {
                eventLog1.WriteEntry("Service start fail. " + e, EventLogEntryType.Error, 11);
            }
            eventLog1.WriteEntry("Service start Success.", EventLogEntryType.Information, 10);

            // Update the service state to Running.
            serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING;
            SetServiceStatus(this.ServiceHandle, ref serviceStatus);
        }

        protected override void OnStop()
        {
            eventLog1.WriteEntry("In onStop.");

            // Update the service state to Start Pending.
            ServiceStatus serviceStatus = new ServiceStatus();
            serviceStatus.dwCurrentState = ServiceState.SERVICE_STOP_PENDING;
            serviceStatus.dwWaitHint = 100000;
            SetServiceStatus(this.ServiceHandle, ref serviceStatus);

            serialPortActions.Close();

            // Update the service state to Running.
            serviceStatus.dwCurrentState = ServiceState.SERVICE_STOPPED;
            SetServiceStatus(this.ServiceHandle, ref serviceStatus);
        }
        protected override void OnContinue()
        {
            eventLog1.WriteEntry("In OnContinue.");
        }
        protected override void OnPause()
        {
            eventLog1.WriteEntry("In OnPause.");
        }
        protected override void OnShutdown()
        {
            eventLog1.WriteEntry("In OnShutdown.");
        }

        public enum ServiceState
        {
            SERVICE_STOPPED = 0x00000001,
            SERVICE_START_PENDING = 0x00000002,
            SERVICE_STOP_PENDING = 0x00000003,
            SERVICE_RUNNING = 0x00000004,
            SERVICE_CONTINUE_PENDING = 0x00000005,
            SERVICE_PAUSE_PENDING = 0x00000006,
            SERVICE_PAUSED = 0x00000007,
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct ServiceStatus
        {
            public long dwServiceType;
            public ServiceState dwCurrentState;
            public long dwControlsAccepted;
            public long dwWin32ExitCode;
            public long dwServiceSpecificExitCode;
            public long dwCheckPoint;
            public long dwWaitHint;
        };
    }
}

Open in new window


SERIAL PORT CLASS:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Ports;

namespace APPSecurityDoor
{
    /// <summary> 
    /// Interfaces with a serial port. There should only be one instance 
    /// of this class for each serial port to be used. 
    /// </summary>
    class SerialPortActions
    {
        private SerialPort _serialPort = new SerialPort();
        private int _baudRate = 4800;
        private int _dataBits = 8;
        private Handshake _handshake = Handshake.None;
        private Parity _parity = Parity.None;
        private string _portName = "COM2";
        private StopBits _stopBits = StopBits.One;

        private DBActions dbActions = new DBActions();
        private EventLog eventLog2 = new EventLog();

        /// <summary> 
        /// Holds data received until we get a terminAPPr. 
        /// </summary> 
        private string tString = string.Empty;
        /// <summary> 
        /// End of transmition byte in this case EOT (ASCII 4). 
        /// </summary> 
        private byte _terminAPPr = 0x4;

        public int BaudRate { get { return _baudRate; } set { _baudRate = value; } }
        public int DataBits { get { return _dataBits; } set { _dataBits = value; } }
        public Handshake Handshake { get { return _handshake; } set { _handshake = value; } }
        public Parity Parity { get { return _parity; } set { _parity = value; } }
        public string PortName { get { return _portName; } set { _portName = value; } }
        public bool Open()
        {
            eventLog2.Source = "APPSecurityDoorSource";
            eventLog2.Log = "APPSecurityDoorLog";
            try
            {
                _serialPort.BaudRate = _baudRate;
                _serialPort.DataBits = _dataBits;
                _serialPort.Handshake = _handshake;
                _serialPort.Parity = _parity;
                _serialPort.PortName = _portName;
                _serialPort.StopBits = _stopBits;
                _serialPort.DataReceived += new SerialDataReceivedEventHandler(_serialPort_DataReceived);
            }
            catch (Exception e)
            {
                eventLog2.WriteEntry("Fail open serial port. " + e, EventLogEntryType.Error, 201);
                return false;
            }
            eventLog2.WriteEntry("Success open serial port.", EventLogEntryType.Information, 200);
            return true;
        }

        public bool Close()
        {
            eventLog2.Source = "APPSecurityDoorSource";
            eventLog2.Log = "APPSecurityDoorLog";
            try
            {
                _serialPort.Close();
            }
            catch (Exception e)
            {
                eventLog2.WriteEntry("Fail close serial port. " + e, EventLogEntryType.Error, 211);
                return false;
            }
            eventLog2.WriteEntry("Success close serial port.", EventLogEntryType.Information, 210);
            return true;
        }

        void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            eventLog2.Source = "APPSecurityDoorSource";
            eventLog2.Log = "APPSecurityDoorLog";

            //Initialize a buffer to hold the received data 
            byte[] buffer = new byte[_serialPort.ReadBufferSize];

            //There is no accurate method for checking how many bytes are read 
            //unless you check the return from the Read method 
            int bytesRead = _serialPort.Read(buffer, 0, buffer.Length);

            //For the example assume the data we are received is ASCII data. 
            tString += Encoding.ASCII.GetString(buffer, 0, bytesRead);

            eventLog2.WriteEntry("Value: " + tString, EventLogEntryType.Information, 1);

            //Check if string contains the terminAPPr  
            if (tString.IndexOf((char)_terminAPPr) > -1)
            {
                //If tString does contain terminAPPr we cannot assume that it is the last character received 
                string workingString = tString.Substring(0, tString.IndexOf((char)_terminAPPr));
                //Remove the data up to the terminAPPr from tString 
                tString = tString.Substring(tString.IndexOf((char)_terminAPPr));
                
                //Do something with workingString 
                //////Console.WriteLine(workingString);

                eventLog2.WriteEntry("Write to " + workingString, EventLogEntryType.Information, 1000);
                dbActions.Insert(workingString);

            }
        }
    }
}

Open in new window

Nathan VanderwystAsked:
Who is Participating?
 
ste5anConnect With a Mentor Senior DeveloperCommented:
First of all: SerialPort implements IDisposable. Thus your class SerialPortActions must also implement the IDisposable interface. So don't use the immediate instanciation of it

 private SerialPort _serialPort = new SerialPort();

Open in new window


Then drop the EventLog.CreateEventSource call from your service. Cause this means that your service must run with administrative priveleges, which is not necessary. Create the event source in the installer. And use as fallback in your service the application log, when you don't find your log source.

The core of your problem is how a service application works. After the OnStart() the services goes to on Execute(), when this method is left, then the service ends its operations.

Thus you need to place your serial port operations into separate worker thread. Which you can start in the OnStart. Then wait in the execute method (ehe execute method is the synchronous hook for worker code). See Creating a C# Service.
0
 
Nathan VanderwystAuthor Commented:
Thank you.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.