?
Solved

C# Keep Serial Port Open After OnStart in Windows Service

Posted on 2016-09-25
2
Medium Priority
?
231 Views
Last Modified: 2016-09-25
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

0
Comment
Question by:Nathan Vanderwyst
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
2 Comments
 
LVL 35

Accepted Solution

by:
ste5an earned 2000 total points
ID: 41814811
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
 

Author Closing Comment

by:Nathan Vanderwyst
ID: 41815296
Thank you.
0

Featured Post

Docker-Compose to Simplify Multi-Container Builds

Our veteran DevOps Author takes you through how to build a multi-container environment, managed with a single utility in order to simplify your deployments.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

I was prompted to write this article after the recent World-Wide Ransomware outbreak. For years now, System Administrators around the world have used the excuse of "Waiting a Bit" before applying Security Patch Updates. This type of reasoning to me …
In this modest contribution, I want to share with the IT community (especially system administrators, IT Support Engineers and IT Help Desks) about Windows crashes/hangs and how to deal with these particular problems.
This is used to tweak the memory usage for your computer, it is used for servers more so than workstations but just be careful editing registry settings as it may cause irreversible results. I hold no responsibility for anything you do to the regist…
If you’ve ever visited a web page and noticed a cool font that you really liked the look of, but couldn’t figure out which font it was so that you could use it for your own work, then this video is for you! In this Micro Tutorial, you'll learn yo…

719 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question