C# Windows Service - c#

Scenario
I've created a windows service, but whenever I start it, it stops immediately. The service was concieved from a console application that used to subscribe to an event and watch processes on a server. If anything happened to process (i.e. It was killed), then the event would trigger the process to be restarted. The reason I'm telling you this is because the original code used to look like this:
Original Console App Code:
static void Main(string[] args)
{
StartProcess sp = new StartProcess();
//Note the readline that means the application sits here waiting for an event!
Console.ReadLine();
}
Now that this code has been turned into a Windows Service, it is essentially EXACTLY THE SAME. However, the service does not sit there waiting, even with the readline, it just ends.....
New Windows Service Code:
protected override void OnStart(string[] args)
{
ProcessMonitor pm = new ProcessMonitor();
Console.ReadLine();
}
Thoughts
Since the functionality is entirely encapsulated within this single class (It quite literally starts, sets up some events and waits) - How can I get the service to actually sit there and just wait? It seems to be ignoring the readline. However this works perfectly as a console application, it is just far more convenient to have it as a service.

Typically you would want something like this. As Joe mentioned in the comments you want Start to initialize and release control to another thread to make sure that you return within 30 seconds.
private readonly ProcessMonitor processMonitor = new ProcessMonitor();
protected override void OnStart(string[] args)
{
processMonitor.Start();
}
protected override void OnStop()
{
processMonitor.Stop();
}

In a Service there is no concept of a readline - there's no keyboard. I wouldn't be surprised if this is throwing an exception at that call. Have you checked your Application Log?

Well... A service doesn't have a console input/output. So the ReadLine won't stop it from executing.
What does ProcessMonitor do?
Typically, for services your code lives in a thread that monitors whether the service has been stopped or paused.

OnStart() must complete and end successfully for the service to be considered "Started"

Move your Console.ReadLine(); into your ProcessMonitor() constructor, and create your ProcessMonitor inside the constructor for the service. Your OnStart method can be empty. Despite what people are saying the Console methods will NOT crash your service, however it is probably not best practice. I guess the proper way to keep a service running (after your timers are started) is to use a while loop with a Thread.Sleep(60000) inside it.
When I am writing a service I put all the functionality in a Class Library project, then I create a Console Application project to test the service functionality, and then a Windows Service project. Both the Console Application and Windows Service project call one method in the Class Library to start the service functionality. If you use this technique you can call Console.WriteLine in the Class Library which can be viewed when running the Console Application. PS Topshelf is overrated, writing a windows service is not that difficult.
public partial class ProcessMonitor_Service : ServiceBase
{
public ProcessMonitor_Service()
{
InitializeComponent();
ProcessMonitor pm = new ProcessMonitor();
}
protected override void OnStart(string[] args)
{
}
protected override void OnStop()
{
}
}
public class ProcessMonitor
{
public ProcessMonitor()
{
// start timers
Console.ReadLine();
}
}

Related

Windows service programmed in C# can be installed and started, but doesn't "do" what it should?

So basically I'm trying to program a Windows Service in C# under Visual Studio 2017. As the service didn't do what i wanted to do i made a test service with a very rudimentary logic to test if it even does what i want in the OnStart() Method. The reason why i chose to manually write a string into the event log is because i want to make sure that there is no writing permission issue. I mean if the service is installed and started, it should write the line into the log shouldn't it?
So:
namespace WindowsService3
{
public class Program : System.ServiceProcess.ServiceBase
{
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Service1()
};
ServiceBase.Run(ServicesToRun);
}
protected override void OnStart(string[] args)
{
this.EventLog.WriteEntry("Hello");
}
protected override void OnStop()
{
}
}
}
I added a ProjectInstaller, added the service name to it as well as specifying it for LocalSystem. Then i used the following line to install it:
C:\Windows\system32>sc \\10.10.0.122 create TESTService binPath= "C:\temp\service\WindowsService1.exe"
The service can now be seen in the service manager and even started. Without problems... In the eventlog though i don't see the specified line. So it seems the service is correctly started, but it doesn't user the logic in the OnStart() method. I have no idea why. I also tried things like creating a text file via the OnStart() method at first... Also no success. What am i doing wrong? Again, the service IS installed and IS started as the Event log and the service manager suggest.
Edit: My problem is that no code is executed in OnStart at all. I can't write a file, i can't write a log, nothing. The service is just installed and can be started, but i doesn't do anything that is put into OnStart() and i don't know why. My coworkers can't figure it out either.
Edit2: The VS2017 template tells me to make the Program class static. But then I can't use OnStart() in it. But maybe this is my missunderstanding? In my example i made the Program class just public to implement OnStart() but maybe that was the mistake?
You have written your service code in the class Program, but your service that is started is named Service1, probably defined by the template in another file.
Either edit Service1.cs to include your startup code, or edit your Program.cs to start your class:
ServicesToRun = new ServiceBase[]
{
new Program() // <----
};
Putting your service and Program.cs in the same class is uncommon, it's indeed a good approach to keep them separate and have a separate Service class.
Try logging this way and then check in event logger under Application errors:
using (EventLog eventLog = new EventLog("Application"))
{
eventLog.Source = "Application";
eventLog.WriteEntry("Message", EventLogEntryType.Error);
}

Code starts only when Application.Run() is invoked WPF application

I have a method which is invoked in class A and it is defined in class B:
class B{
[STAThread]
public static void ScanForAxisCameras() {
DNSSDService service = new DNSSDService();
DNSSDEventManager eventManager = new DNSSDEventManager();
eventManager.ServiceFound += new _IDNSSDEvents_ServiceFoundEventHandler(eventManager_ServiceFound);
DNSSDService browse = service.Browse(0, 0, "_axis-video._tcp", null, eventManager);
Application.Run();//if not invoked everything above does not start
}
}
class A{ ...before invoking..... B.ScanForAxisCameras(); ....after invoking....}
The code in class B "starts"/works only if I invoke Application.Run(). But it causes that all the code in class A ....after invoking.... method does not work. How to handle it so it will not freeze the application?
Edit: the class A is class MainWindow.xaml.cs. It is WPF application.
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e) {
createGUI();
}
private void createGUI() {
LocalNetworkScanner.ScanForAxisCameras();//when there is no Application.Run() ScanForAxisCameras() does not work.
}
}
The WPF UI thread on which you call ScanForAxisCameras() already has a message loop. I believe the problem with your code is that all objects you create inside ScanForAxisCameras have the local scope:
public static void ScanForAxisCameras() {
DNSSDService service = new DNSSDService();
DNSSDEventManager eventManager = new DNSSDEventManager();
eventManager.ServiceFound += new _IDNSSDEvents_ServiceFoundEventHandler(eventManager_ServiceFound);
DNSSDService browse = service.Browse(0, 0, "_axis-video._tcp", null, eventManager);
Application.Run();//if not invoked everything above does not start
}
Without Application.Run(), your objects (service, eventManager, browse) may be getting destroyed and finalized as soon as ScanForAxisCameras finishes. So, the events you're looking for (like ServiceFound) may not even have a chance to get fired.
If you call Application.Run(), then ScanForAxisCameras doesn't exit (at least not until Application.Run() itself exits). That keeps your objects alive and functional.
Try refactoring your code to keep the references to these objects in member fields of your class (or in static variables, FWIW). I believe that should fix the problem.
[EDITED] On a side note, the [STAThread] attribute doesn't make sense in that context (unless you use ScanForAxisCameras as an entry point for a new thread - apparently, that's not the case here).
You can introduce a new Run()-method in your App in the App.xaml.cs file.
Here you can perform custom actions before the application itself gets initialized.
Further information here.
public partial class App : Application
{
public new void Run()
{
// Do your stuff here
B.DoStuff();
// Call the base method
base.Run();
}
}
Application.Run starts the message loop for that particular thread, if there is no message loop then there is no notification for your objects to know they have to do something.
The code in class B "starts"/works only if I invoke Application.Run(). But it causes that all the code in class A ....after invoking.... method does not work.
Run is a blocking call therefore any code after that call is not reachable until the application is closing down i.e. when you exit the message loop.
How to handle it so it will not freeze the application?
In short, you can't. Run will always block so any code you need to run as part of your application startup will have to happen before the call.
After your edit to mention that this is a WPF application then Application.Run as a static method is not the right way to go here. If you need to run initialization when your application starts then you can do what has already been suggested and override the Run method of the Application class, or alternatively (maybe more appropriately) you can hook into the OnStartup event e.g.
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
// code before startup
base.OnStartup(e);
// code after startup
}
}
It seems as though in this situation you would need to add Application.Run() in a different class. Run() accepts nothing, ApplicationContext, Form. This controls the lifetime of the application and should be called before class A, unless class A is the entry point.
See http://msdn.microsoft.com/en-us/library/ms157900.aspx for details.
A Windows Forms application starts when the Main method is called. You can implement initialization procedures on the Main function. However, to initialize a Windows Forms application fully and start it routing Windows Forms events, you need to invoke Application.Run.
you can read about Application here

How do i know if my windows service is working?

I have built a windows service to populate a database with my email inbox every 5 minutes.
I used a class inside my windows service the class gets my emails and writes them to my database, the class has been tested and works.
All i need the windows service to do is use a timer and call the class every 5 minutes, but i have no idea whats going on as i cant even test my windows service.
Please someone tel me what to do to test, if there is a way to test, or just blink luck and pray it works lol.
Also do u have to uninstall and re-install every time you want to test the service or is there an update service option? Please answer this i'm really interested even tho its not my main question.
This is my windows service, if u can point out any errors that would be amazing since i cant test for them. I think my timer might be wrong if some one could look at it?
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.Timers;
namespace EmailWindowsService
{
public partial class MyEmailService : ServiceBase
{
private Timer scheduleTimer1 = null;
private DateTime lastRun;
private bool flag;
public MyEmailService()
{
InitializeComponent();
if (!System.Diagnostics.EventLog.SourceExists("MySource"))
{
System.Diagnostics.EventLog.CreateEventSource(
"MySource", "MyNewLog");
}
eventLogEmail.Source = "MySource";
eventLogEmail.Log = "MyNewLog";
scheduleTimer1 = new Timer();
scheduleTimer1.Interval = 5 * 60 * 1000;
scheduleTimer1.Elapsed += new ElapsedEventHandler(scheduleTimer_Elapsed);
}
protected override void OnStart(string[] args)
{
flag = true;
lastRun = DateTime.Now;
scheduleTimer.Start();
eventLogEmail.WriteEntry("Started");
}
protected override void OnStop()
{
scheduleTimer.Stop();
eventLogEmail.WriteEntry("Stopped");
}
protected override void OnPause()
{
scheduleTimer.Stop();
eventLogEmail.WriteEntry("Paused");
}
protected override void OnContinue()
{
scheduleTimer.Start(); ;
eventLogEmail.WriteEntry("Continuing");
}
protected override void OnShutdown()
{
scheduleTimer.Stop();
eventLogEmail.WriteEntry("ShutDowned");
}
protected void scheduleTimer_Elapsed(object sender, ElapsedEventArgs e)
{
RetriveEmailClass Emails = new RetriveEmailClass();
if (flag == true)
{
eventLogEmail.WriteEntry("In getting Email Method");
Emails.ServiceEmailMethod();
lastRun = DateTime.Now;
flag = false;
}
else if (flag == false)
{
if (lastRun.Date < DateTime.Now.Date)
{
Emails.ServiceEmailMethod();
eventLogEmail.WriteEntry("In getting Email Method");
}
}
}
}
}
Surely you can test it. All you need is
start up the service
observe that it triggers the expected call after 5 minutes
(observe that it triggers the expected call every 5 minutes for a couple more times)
You can test this manually, or (preferably) create/use an automated test harness which allows you to test repeatedly and reliably, as many times as you want. This is possible even using a simple batch file.
To detect that the timer works correctly, you can inspect its log file. It also helps of course if you make the called class method configurable instead of hardcoding it. So you can run your automated tests using a dummy worker class which does not flood your inbox :-)
To make it even more testable, you can extract the timing logic from your service class too, so that it becomes runnable from a regular application. Then you can test it even easier, even using a unit test framework such as NUnit. This allows you to do more thorough testing, using different timing intervals etc. And the service class itself becomes an almost empty shell whose only job is to launch and call the other classes. If you have verified that all the classes containing real program logic (i.e. all the code which can fail) is unit tested and works fine, you can have much greater confidence in that your whole app, when integrated from its smaller parts, works correctly too.
Update
Looking through your code, it seems that you don't initialize flag anywhere, so its default value will be false. You should initialize it to true in the constructor, otherwise your email retriever won't ever get called even if the timer fires properly.
To set the interval to 1 minute, my first guess would be
scheduleTimer1.Interval = 1 * 60 * 1000;
James Michael Hare has on his blog written about a really nice template/framework he has made, making it lot easier to develop (and debug) Windows Services: C# Toolbox: A Debuggable, Self-Installing Windows Service Template (1 of 2)
It provides you with all the basics you need to quickly get started. And best of all, it give you a really nice way to debug your service as if it was a regular console application. I could also mention that it provides out of the box functionality to install (and uninstall) your service. Part two of the post can be found at this link.
I've used this myself a couple of times, and can really recommend it.
Refactor you logic in another class.
Write a simple console application invoking this class
Test it like a normal application.
Once it runs standalone, it should run as a service.
Beware on permissions and service registration, there are a couple of issues there (like having a sys user, or a desktop session).
A good practice is to use system logs (e.g. the ones you can inspect with eventvwr)
1.add this line to the place you want to break, then you can debug your service.
System.Diagnostics.Debugger.Break();
or
2.try to attach to your service progress from process explorer, then you can also debug your service.
or
3.use a log file to log what your service is doing.
You can attach a debugger to you running service instance from Visual Studio. Click "Debug" in the main menu, "Attach to Process...", select your service process from the list and click "Attach".
If you need to debug the startup of your service, you need to use System.Diagnostics.Debugger.Break().

How do I generate keystrokes in a non-form application

So I have a huge program and decided I should make one of the methods run in a separate thread. So I put the method in a separate class, an activated it on my form. It seemed to worked just how I wanted it to until it got to part where it gave me this error:
SendKeys cannot run inside this application because the application
is not handling Windows messages. Either change the application to
handle messages, or use the SendKeys.SendWait method.
I tried looking for the answer online. I think I saw something about how SendKeys only works in a Form or something.
Can anyone tell me a way to simulate a keystroke without using SendKeys, OR a way to get SendKeys to work in a different, non-form thread?
Your console application needs a message loop. This is done through the Application class. You will need to call Application.Run(ApplicationContext).
class MyApplicationContext : ApplicationContext
{
[STAThread]
static void Main(string[] args)
{
// Create the MyApplicationContext, that derives from ApplicationContext,
// that manages when the application should exit.
MyApplicationContext context = new MyApplicationContext();
// Run the application with the specific context. It will exit when
// the task completes and calls Exit().
Application.Run(context);
}
Task backgroundTask;
// This is the constructor of the ApplicationContext, we do not want to
// block here.
private MyApplicationContext()
{
backgroundTask = Task.Factory.StartNew(BackgroundTask);
backgroundTask.ContinueWith(TaskComplete);
}
// This will allow the Application.Run(context) in the main function to
// unblock.
private void TaskComplete(Task src)
{
this.ExitThread();
}
//Perform your actual work here.
private void BackgroundTask()
{
//Stuff
SendKeys.Send("{RIGHT}");
//More stuff here
}
}
I Know this not an answer, but this how i used to do using ActiveX and Script
Set ws = CreateObject("WScript.Shell")
str = "Hi there... ~ Dont click your mouse while i am typing." & _
" ~~This is a send key example, using which you can send your keystrokes"
ws.Run("notepad.exe")
WScript.Sleep(1000)
For c=1 To Len(str)
WScript.Sleep(100) 'Increase the value for longer delay
ws.SendKeys Mid(str,c,1)
Next
Save this code as file.vbs and double click to execute in windows machine.

Facing Error 1053 in case of windows service

I made a windows service. My code is similar to:
onstart()
{
while(true)
{
//code
}
}
I am trying to update my database every minute.
The service builds properly and installed properly, but when I start the service it gives error 1053.
I have tried alot to solve it but not get any solution.
protected override void OnStart(string[] args)
{
//put debugger.launch the first statement of OnStart...and make sure you running in debug mode
Debugger.Launch();
try
{
//your code
.
.
.
}
catch(Exception ex)
{
//see what exception is coming
}
}
Did you define onStop?
The ServiceBase class calls the OnStop
method directly from the Service
command handler that is defined in the
ScDispatcherLoop of the Advapi32.dll
file. After 30 seconds, if the
ScDispatcherLoop thread is not ready
to receive a new service command from
the service control manager, Windows
Service Controller marks the service
as "time out." Therefore, you receive
this error message.
-Microsoft
Go to the console of the server (in the server room if you will) and start the Window Service. Remote in won't work.

Categories

Resources