Solved

C# Keep Serial Port Open After OnStart in Windows Service

Posted on 2016-09-25
2
55 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
2 Comments
 
LVL 33

Accepted Solution

by:
ste5an earned 500 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

Master Your Team's Linux and Cloud Stack

Come see why top tech companies like Mailchimp and Media Temple use Linux Academy to build their employee training programs.

Question has a verified solution.

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

When you start your Windows 10 PC and got an "Operating system not found" error or just saw  "Auto repair for startup" or a blinking cursor with black screen. A loop for Auto repair will start but fix nothing.  You will be panic as there are no back…
Learn how to PXE Boot both BIOS & UEFI machines with DHCP Policies and Custom Vendor Classes
This Micro Tutorial hows how you can integrate  Mac OSX to a Windows Active Directory Domain. Apple has made it easy to allow users to bind their macs to a windows domain with relative ease. The following video show how to bind OSX Mavericks to …
Windows 10 is mostly good. However the one thing that annoys me is how many clicks you have to do to dial a VPN connection. You have to go to settings from the start menu, (2 clicks), Network and Internet (1 click), Click VPN (another click) then fi…

821 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