Go Premium for a chance to win a PS4. Enter to Win

x
?
Solved

C# Keep Serial Port Open After OnStart in Windows Service

Posted on 2016-09-25
2
Medium Priority
?
306 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 36

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

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Question has a verified solution.

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

Know the reasons and solutions to move/import EDB to New Exchange Server. Also, find out how to recover an Exchange .edb file and to restore the file back.
Unable to change the program that handles the scan event from a network attached Canon/Brother printer/scanner. This means you'll always have to choose which program handles this action, e.g. ControlCenter4 (in the case of a Brother).
With the advent of Windows 10, Microsoft is pushing a Get Windows 10 icon into the notification area (system tray) of qualifying computers. There are many reasons for wanting to remove this icon. This two-part Experts Exchange video Micro Tutorial s…
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…

916 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