Solved

C# Windows Service will not listen

Posted on 2013-06-25
21
407 Views
Last Modified: 2013-06-28
I have a C# Windows Service that runs successfully when the service is started. After it runs through the first cycle and completes all of the records from the database table.

Once a new record is delivered on the table it does not do anything. It's running but I do not know what is happening.

Here is what I have:
1. C# Window Server Framework 4.0
2. Service runs and seeks for new records in database table
3. Moves data to a different table location
4. Repeats process.
5. Once database table is empty the service keeps running
6. When a new file is ready in the table the service does not work any longer.

Code included below..

 protected override void OnStart(string[] args)
        {
           

                try
                {
                   

                    _timer.AutoReset = true;
                    _timer.Interval = 10000;  // 10 seconds
                    _timer.Elapsed += OnElapsedEvent;
                    _timer.Start();

                    Constants.Constants.CurrentQueue = "RTD";

                    ThreadStart st = new ThreadStart(ReaderDemo.ReaderMain);
                    threadListener = new Thread(st);


                    servRun = true;


                    string[] lines42 = { "run", "started", "here" };
                    System.IO.File.WriteAllLines(@"C:\Service\started.txt", lines42);

                    threadListener.Start();


                }


                catch (Exception ex)
                {

                    string ErrorMessage = ex.Message;

                }

         

        }

 protected override void OnStop()
        {

            servRun = false;


            string[] lines4 = { "MQProcessing", "started", "here" };
            System.IO.File.WriteAllLines(@"C:\Service\Stop.txt", lines4);

            _timer.Stop();

        }

Any suggestions?

Thanks
0
Comment
Question by:jeffreyjseaman
[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
  • 11
  • 10
21 Comments
 
LVL 57

Expert Comment

by:Julian Hansen
ID: 39274913
What does this do
ReaderDemo.ReaderMain

Open in new window

It might be that your thread is terminating after it has finished processing the database when it should be sitting in an efficient wait state before checking the database again.
0
 

Author Comment

by:jeffreyjseaman
ID: 39275029
Sorry, The ReaderDemo will read what is in the table and then Save it via a Stored Procedure call. The Stored Procedure will "Insert the records" from the table into an archive table and keep looping until finished.

Here is the Reader Information..
 class ReaderDemo
    {

        public static void ReaderMain()
        {
            ReaderDemo rd = new ReaderDemo();

            if ((Constants.Constants.CurrentQueue == "RTD"))
            {

                rd.LoadEWS();


            }

        }



        public void LoadEWS()
        {
            SqlDataReader rdr = null;
            SqlConnection con = null;
            SqlCommand cmd = null;

            try
            {


                string ConnectionString = ConfigurationManager.ConnectionStrings["TestConn"].ConnectionString;

                con = new SqlConnection(ConnectionString);
                con.Open();

                // Set up a command with the given query and associate
                // this with the current connection.
                string CommandText = "SELECT RowNumber,LastName,FirstName,City,State,Zipcode" +
                                     "  FROM Customers ";
                cmd = new SqlCommand(CommandText);
                cmd.Connection = con;

                // Execute the query
                rdr = cmd.ExecuteReader();

                while (rdr.Read())
                {


                    int strRowNumber = (int)rdr["RowNumber"];
                    string strLastName = (string)rdr["LastName"];
                    string strFirstName = (string)rdr["FirstName"];
                    string strCity = (string)rdr["City"];
                    string strState = (string)rdr["State"];
                    string strZip = (string)rdr["ZipCode"];

                    //Call Save Row to table
                    Save.Saverow(strRowNumber, strLastName, strFirstName, strCity, strState, strZip);


                }
            }
            catch (Exception ex)
            {

                // Print error message
                //MessageBox.Show(ex.Message);
            }
            finally
            {
                // Close data reader object and database connection
                if (rdr != null)
                    rdr.Close();

                if (con.State == ConnectionState.Open)
                    con.Close();


            }
        }


    }
}



Here is the Save routine call if this helps you better..

 class Save
    {



        public static void Saverow(
                                    int strRowNumber, String strLastName, String strFirstName, String strCity, String strState,
                                    String strZipCode
                                  )
        {




            string conn = ConfigurationManager.ConnectionStrings["TestConn"].ConnectionString;
            System.Data.SqlClient.SqlConnection SqlConn = new SqlConnection(conn);

       

            SqlConn.Open();



            SqlCommand SqlCmd = new SqlCommand();
            SqlCmd.Connection = SqlConn;
            SqlCmd.CommandText = "dbo.ArchiveData";
            SqlCmd.CommandType = CommandType.StoredProcedure;


            SqlCmd.Parameters.AddWithValue("RowNumber", strRowNumber);
            SqlCmd.Parameters.AddWithValue("LastName", strLastName);
            SqlCmd.Parameters.AddWithValue("FirstName", strFirstName);
            SqlCmd.Parameters.AddWithValue("City", strCity);
            SqlCmd.Parameters.AddWithValue("State", strState);
            SqlCmd.Parameters.AddWithValue("ZipCode", strZipCode);
       
       


            try
            {
                SqlCmd.ExecuteNonQuery();
               
            }
            catch (Exception ex)
            {

                string ErrorMessage = ex.Message;
               
            }
            finally
            {
                SqlConn.Close();
                //MQExecutable.Logging.AuditLog("MQMessages Saved to the Database");
            }

            //Return UserID
        }


    }
}

Thanks
0
 
LVL 57

Expert Comment

by:Julian Hansen
ID: 39275170
Ok but what happens when the function LoadEWS terminates?

It is going to return to the point where the thread was created and the thread will terminate so the service will no longer have a running thread.

You have to your thread into a wait state after it has finished processing the DB stuff so that the thread remains active - the thread should wait on a timeout and / or a signal from the service stop function that the service is stopping.
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

Author Comment

by:jeffreyjseaman
ID: 39275691
The LoadEWS function finishes it just keeps running via Visual Studios 2010. I have put break points in and nothing gets stopped and it seems to keep running but nothing is happening.

Yes it's suppose to get to the point to kill the thread. I have this in the stop routine.

Let me try the thread into a wait state and I will update you if that fixes it. Thanks for your help.
0
 
LVL 57

Expert Comment

by:Julian Hansen
ID: 39276176
I think you are misunderstanding me.

Your LoadEWS basically does the following

* define query
* run query
* loop through returned rows
* cleanup
* exit <==== Here is your problem

The function returns and the thread terminates.

you need an outer block that has some sort of efficient wait condition

* while true
  * wait for timeout or stop signal
  * if stop signal break
  * define query
  * run query
  * loop through returned rows
  * cleanup
* end while
* exit
0
 

Author Comment

by:jeffreyjseaman
ID: 39276239
Thanks for the clarification. You're saying a while boolean statement at the beginning of the statement before the thread is started? perform a stop signal after the cleanup, that way if it gets more data it can be started again. Is that pretty much what you are referring to?

Thanks
0
 
LVL 57

Expert Comment

by:Julian Hansen
ID: 39277108
No what I am saying is that the thread needs to stay running for the duration of the service. Your current implementation will result in the thread terminating after it has completed the database operation.

You put a while statement around the code you want to run as part of the service.

Inside the while you use one of the wait event functions to wait for a signal or a timeout.

If a timeout occurs the database code will run and when done the while statement will cycle the thread into the wait statement.

Checking for a service stop after the wait terminates is necessary for graceful shutdown of the service. In the code you have posted you are setting a variable "servRun" which I assume is to indicate the service is running / stopped - but you don't appear to use it.

You should put a test for this inside the while immediately after the wait.

You should also put a event signal call inside the stop to wake up the thread so it can terminate.
0
 

Author Comment

by:jeffreyjseaman
ID: 39278320
JulianH, I attached my source code. I put a Thread.Sleep command in here but in debug it works fine but when I compile the source code to release and try to start the service it doesn't like it.

You will be rewarded the points for your help either way. If you can take a peak at my code and give me an idea what I can do to make it work. Thanks a lot.
0
 
LVL 57

Expert Comment

by:Julian Hansen
ID: 39278514
Ok, leave it with me for a bit - I don't have access to my compiler right now but as soon as I do I will see if I can knock something together based on your code.
0
 

Author Comment

by:jeffreyjseaman
ID: 39278856
THanks JulianH. Well appreciated
0
 
LVL 57

Expert Comment

by:Julian Hansen
ID: 39279155
Can you post all your code?

You have a timer event pointing to OnElapsedEvent but no definition for that - will be easier if I don't have to create all from scratch.
0
 

Author Comment

by:jeffreyjseaman
ID: 39279346
I attached the files. I couldn't include suo, sln etc; because it doesn't allow you to do that.

I also posted the full zip file of the service if it helps better here.
http://www.ringgoldaysa.org/services/WindowsService.zip

Thanks a lot
WindowsService-3.zip
0
 
LVL 57

Expert Comment

by:Julian Hansen
ID: 39280510
Ok you should be able to do this

Service.cs
  // Add this member
  public static AutoResetEvent threadWait = new AutoResetEvent(false);
  ...
  // In the stop method add this 
protected override void OnStop()
{
  servRun = false;
  threadWait.Set();
  ...

Open in new window

Then in Reader.cs change ReaderMain to this
        public static void ReaderMain()
        {
            ReaderDemo rd = new ReaderDemo();

            if ((Constants.Constants.CurrentQueue == "RTD"))
            {
                // Work until told to stop
                while(true) {
                    // if signalled then we are done - signalled in Stop method
                    if (Service1.threadWait.WaitOne(20000)) {
                        break;
                    }
                    // if timeout then do work
                    else {
                        rd.LoadEWS();
                        Service1.threadWait.Reset();
                    }
                }
            }
        }

Open in new window

That should do it
0
 
LVL 57

Expert Comment

by:Julian Hansen
ID: 39280529
And remove the While loop from the Start process - you are just going to end up creating millions of threads ...
0
 
LVL 57

Expert Comment

by:Julian Hansen
ID: 39280540
Hang fire on the above - a few issues I have found ...
0
 

Author Comment

by:jeffreyjseaman
ID: 39280907
Thanks I will try this when I get home tonight.
0
 
LVL 57

Accepted Solution

by:
Julian Hansen earned 500 total points
ID: 39282270
Ok I have put together a bit of code you can work with. There are two approaches.

1. Create a timer in your OnStart event and set this to call your worker thread every Interval

The cons of this is that if your worker thread takes longer than interval to process you end up with worker processes bumping into each other .

2. The method I proposed above which is to use signalling.

I have posted a project that uses the latter

Another key aspect that was missed is that worker threads and shared resources must be static - worker thread must be static and anything that is shared between the worker thread and the service must be static.

SLN files are zippable - you must first close VS before zipping your project see attached.

In the attached project Add the call to your ReaderDemo.main in the Else part of the DoWork method. That should do it.
EEService.zip
0
 

Author Comment

by:jeffreyjseaman
ID: 39283923
Thank You so much... I am going to try now.. Either way you are getting the points.. super job
0
 

Author Comment

by:jeffreyjseaman
ID: 39285733
JulianH, Thank You so much.. This worked beautifully. If there is a way I can reward you additional 500 points let me know because your efforts were absolutely amazing.. Thank You...
0
 

Author Closing Comment

by:jeffreyjseaman
ID: 39285737
Excellent support. Provided me exactly what I was looking for. Excellent assistance.
0
 
LVL 57

Expert Comment

by:Julian Hansen
ID: 39285830
You are most welcome - thanks for the points.
0

Featured Post

Salesforce Made Easy to Use

On-screen guidance at the moment of need enables you & your employees to focus on the core, you can now boost your adoption rates swiftly and simply with one easy tool.

Question has a verified solution.

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

Just a quick little trick I learned recently.  Now that I'm using jQuery with abandon in my asp.net applications, I have grown tired of the following syntax:      (CODE) I suppose it just offends my sense of decency to put inline VBScript on a…
In .NET 2.0, Microsoft introduced the Web Site.  This was the default way to create a web Project in Visual Studio 2005.  In Visual Studio 2008, the Web Application has been restored as the default web Project in Visual Studio/.NET 3.x The Web Si…
Nobody understands Phishing better than an anti-spam company. That’s why we are providing Phishing Awareness Training to our customers. According to a report by Verizon, only 3% of targeted users report malicious emails to management. With compan…
This video shows how to use Hyena, from SystemTools Software, to update 100 user accounts from an external text file. View in 1080p for best video quality.

732 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