As a First step I created Windows Service project configured it properly and
On second Step I have added TopShelf Version 3.1.135.0 in my project If I run my service through (F5 Run) then it is loading Top-shelf Console and service is completed successfully.
However When I am running it to install and Start it from command prompt I am having below TimeOut Error.
Topshelf.Hosts.StartHost Error: 0 : The service failed to start., System.Service
Process.TimeoutException: Time out has expired and the operation has not been co
mpleted.
public class AppService
{
LoggingService loggingService = new LoggingService(typeof(AppService).Name);
public void Start()
{
loggingService.Info("SampleService is Started");
ExtractProcess.Start();
TransformProcess.Start();
}
public void Stop()
{
loggingService.Info("SampleService is Stopped");
}
}
-- Updated Code to fix this issue
public void Start()
{
loggingService.Info("MPS.GOA.ETLService is Started");
ThreadStart myThreadDelegate = new ThreadStart(StartService);
Thread myThread = new Thread(myThreadDelegate);
myThread.Start();
}
private void StartService()
{
timer.Elapsed += new System.Timers.ElapsedEventHandler(OnElapsedTime);
timer.Interval = 60000 * ServiceIntervalInMinutes; //1 minute 60000 milliseconds
timer.Enabled = true;
Process();
}
private void Process()
{
ExtractProcess.Start();
TransformProcess.Start();
}
Any Suggestions?
This error is happening because you are running the extract and process methods in the Start method of the service. This is OK in Visual Studio, but when you install the service and start it, the Service Control Manager waits for the Start method to return, and if it does not do so within a certain time (30 seconds by default) then it will return this error.
You have several options, all of which will allow the Start method to return immediately:
Invoke the extract and transform methods on a separate thread
Invoke the extract and transform methods asynchronously
Use a timer to start the extract and transform process
In case you (like me) is struggling to get the service to start - and all you've found so far is references to starting work in a separate thread (and you already did) this might be the solution right here..
My problem was that I had an external JSON config file being read from the project's directory path. What I needed was to get the assembly path, so that when the .NET application is published and installed with Topshelf - it looks for the config file at the right place.
string assemblyPath = Path.GetDirectoryName(typeof(MyConfigManagerClass).Assembly.Location);
var builder = new ConfigurationBuilder()
.SetBasePath(assemblyPath)
.AddJsonFile("config.json", optional: false);
myConfigurationObject = builder.Build();
Topshelf gave an error saying the service couldn't be started, but now I finally know why.
In my case it was neither of the above solutions that solved it, but actual permissions within the topshelf service, that required access to a file that resided in an external server.
TopShelf program running on test server
Log file located on Production server
Test server does not have access to external servers, for security
reasons.
So I changed the program to refer everything internally inside it's own server, and it worked fine.
Related
I have written a simple code that should start automatically using topshelf. The problem is that it starts without any problems if I run it from Visual Studio or when I just click in debug folder on exe file, but.... when I install it as a service it does nothing...
So here is my code
using System;
using System.Collections.Generic;
using Topshelf;
using System.IO;
namespace ConsoleCRP
{
public class Program
{
public static void Main(){
var rc = HostFactory.Run(x =>{
x.Service<ClientReportingScheduler>(s =>
{
s.ConstructUsing(name => new ClientReportingScheduler());
s.WhenStarted(tc => tc.Start());
s.WhenStopped(tc => tc.Stop());
});
x.StartAutomatically();
x.RunAsLocalSystem();
x.SetDescription("This is automatic scheduler");
x.SetDisplayName("scheduler");
x.SetServiceName("servicetest");
});
var exitCode = (int)Convert.ChangeType(rc, rc.GetTypeCode());
Environment.ExitCode = exitCode;
}
}
public class ClientReportingScheduler
{
public System.Timers.Timer _timer;
public int startJob = 0;
public ClientReportingScheduler()
{
startJob = 1000 * 5 ;
_timer = new System.Timers.Timer(startJob) { AutoReset = true };
_timer.Elapsed += (sender, eventArgs) =>
{
Console.WriteLine("Starting scheduler query check at {0}" + Environment.NewLine, DateTime.Now);
};
}
public void Start()
{
_timer.Start();
}
public void Stop()
{
_timer.Stop();
}
}
}
I did copy the sample from topshelf documentation and there are only some small changes to that code. I have pasted here only the light version of this (just to show you the problem). In the full version I used as well fluentscheduler and there is a lot of code that connects to postgress, get some data and do some stored procedures, but this is not the problem. The problem is that even this small code that you can see above doesn't work at all. You can create this program now if you start new console application and just paste it there (and of course add topshelf from nuget). If you start it, it should work, but if you install this as service this is not working at all...
Here are as well steps that I did when installing as service:
1) open CMD as administrator
2) go to the directory with debug
3) install it that way: ConsoleCRP.exe install
4) start the service: net start servicetest
5) if you want to stop the service: net stop servicetest
6) if you want to unistall: ConsoleCRP.exe uninstall
Here is the result:
Configuration Result:
[Success] Name servicetest
[Success] DisplayName scheduler
[Success] Description This is automatic scheduler
[Success] ServiceName servicetest
Topshelf v4.2.1.215, .NET Framework v4.0.30319.42000
The servicetest service is now running, press Control+C to exit.
Starting scheduler query check at 08/10/2019 16:31:12
Starting scheduler query check at 08/10/2019 16:31:17
Starting scheduler query check at 08/10/2019 16:31:22
Starting scheduler query check at 08/10/2019 16:31:27
So tell me, what is wrong in that code that it just doesn't work as a service...??
thank you in advanced for your help!
I found the problem...
There were 2 problems.
First is that when this is service it will not open console application at all and if you want to see anything you must write it i.e. to txt file...
second (on my full project) was that I was writing everything using my own logger, which was writing file using AppDomain.CurrentDomain.BaseDirectory... it seems like this is not allowed when this is serive and you have to write the log in some other place.
and actually when I solved all of this I found as well that this is also not possible for service to open files that are on some servers... so if there are any paths like \server\folder\subfolder\file.txt service will not open that file... at least in my case.
So I guess that case is solved, but maybe someone who had this problem will see this answer and it will help....
I'm trying to run some console app as windows service, I followed this question, and I made a few changes to make it fit to my app.
My main code looks like that:
public static class Program
{
public class Service : ServiceBase
{
public Service(string serviceName)
{
this.ServiceName = serviceName;
}
protected override void OnStart(string[] args)
{
Program.Start(args);
}
protected override void OnStop()
{
Program.Stop(this.ServiceName);
}
}
#endregion
static void Main(string[] args)
{
if (!Environment.UserInteractive)
// running as service
using (var service = new Service("TestService"))
ServiceBase.Run(service);
else
{
// running as console app
Start(args);
}
}
private static void Start(string[] args)
{
while(true)
{
//DO SOMTHING
}
}
private static void Stop(string serviceName)
{
//Writing to log that 'serviceName' stopped
}
}
I tried to run the following console app as a service, by using the following steps:
1) Use the command: sc create ServiceTestName123 binPath= "PATH TO THE EXE FILE IN THE PROJECT DEBUG FOLDER".
2) Use the command: sc start ServiceTestName123 "parameter1".
And I got an error:
"StartService FAILED 1053:
The service did not respond to the start or control request in a timely fashion"
I read about the error in the internet and found out that I can try to solve this problem by running the start function with another thread, so I updated the OnStart function to the following function:
protected override void OnStart(string[] args)
{
Thread t = new Thread(() => Program.Start(args));
t.Start();
}
After trying to re-create the service (delete the old one and create the service again with the new OnStart function) and re-run it I got the same error.
By the way, when I ran this code as console app it worked properly.
Could someone please explaing me what am I doing wrong?
Thanks a lot.
I tried your exact steps and it worked for me. I will highlight a few key points that I came across
OnStart should definitely return in a timely fashion. i.e. the work should happen in a separate process/thread. I used your code for thread and it worked fine for me.
Make sure the executable is on a local drive which can be accessed from your "Local System" account without any permission issues.
Make sure when you create the service, you provide the absolute path and not relative path.
Make sure the sc create ... command responds back with [SC] CreateService SUCCESS
Check the service was created in the service control panel
Make sure you can start it from the service control panel before attempting it from command line
Also, open task manager or process explorer to make sure the service executable is running or not (irrespective of what status is returned by service control panel or scm start)
For debugging, I logged the information into a local temp file - again watch out for permissions issues with Local System account.
If for whatever reasons you have to delete the service, make sure that it indeed disappeared from the service control panel
To debug what's going on, you can attach the debugger at the very beggining of your program start up.
This way, you can check what your program is doing.
You can also check in the Windows ever viewer, the error that windows is throwing.
Put this line at the start trace of your program:
System.Diagnostics.Debugger.Launch()
NOTE: I'm not doing anything similar to Topshelf installer requires me to press enter twice - why?
Service class (interesting parts):
public class ServiceCore
{
public ServiceCore(ServiceRuntimeConfiguration serviceRuntimeConfiguration)
{
_runningTasks = new List<Task>();
}
public bool Start(HostControl hostControl)
{
_hostControl = hostControl;
_messageProcessor.Start(); // Starts a System.Threading.Tasks.Task
StartListener(); // starts a System.Threading.Tasks.Task
return true;
}
}
Program.cs:
Host host = HostFactory.New(configurator =>
{
configurator.UseNLog();
// Configure core service
configurator.Service<ServiceCore>(svc =>
{
svc.ConstructUsing(theService => new ServiceCore(_serviceRuntimeConfiguration));
svc.WhenStarted((svc, hostControl) => svc.Start(hostControl));
svc.WhenStopped((svc, hostControl) => svc.Stop(hostControl));
});
// Configure recovery params
configurator.EnableServiceRecovery(recoveryConfigurator =>
{
recoveryConfigurator.RestartService(0);
recoveryConfigurator.OnCrashOnly();
recoveryConfigurator.SetResetPeriod(1);
});
// Execute HostConfigurator
host.Run();
}
The Problem
When I do this:
MyService.exe install --manual --localsystem
The service installs fine, but the command never returns:
Running a transacted installation.
Beginning the Install phase of the installation. Installing service
NotificationEngine.Main... Service NotificationEngine.Main has been
successfully installed.
The Install phase completed successfully, and the Commit phase is
beginning.
The Commit phase completed successfully.
The transacted install has completed.
^C (I have to press CTRL+C)
What should I do for the install command to complete and then return?
NOTE The same behaviour is observable if I run help (i.e. help displays but the command does not return):
MyService.exe help
Generally this means you don't release control of some resource and the process can't cleanly exit. However, this stuff is complicated, so it's hard to say for sure.
A few things I would try
What happens when you execute MyService start after an install/CTRL+C? I'm assuming it also blocks since help does.
Check logging, do you have any enabled? Is there file contention or permissions issues?
What else does your Main() entry point do? Is it doing something that after host.Run()? Your code above makes it looks like you're calling it from within the construction of that object, but I assume it's bad cut-n-pasting.
Make sure you aren't initializing resources before the ConstructUsing and When* callbacks are fired.
After this, I would take this to our mailing list at https://groups.google.com/forum/#!forum/topshelf-discuss.
ServiceCore : ServiceBase
The type T specified in the configurator.Service should subclass ServiceBase.
This fixed the problem for a service that would install fine but hang on the last step of install/uninstall.
I have created a Windows Service in Visual Studio 2012 that should run 2 services when executed.
static void Main()
{
// creates an array to hold all services that will run in this application
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
// services added here
new Service1(),
new Service2()
};
ServiceBase.Run(ServicesToRun);
}
I have already read various possible answers in various forums, etc. but I have not found a working solution for me.
I have added a Service Installer for each service and I also have one project installer.
When I install the service through the Developer Command Prompt I can see the Windows Service in the Computer Manager but I can also see Service1 and Service2.
When I run the Windows Service, it will only run Service1 and not Service2. However, both Service1 and Service2 can be started individually.
Anyone any suggestions? Been stuck on this for some time now!
EDIT
In the Service1 and Service2 OnStart() I have the following code:
CheckService();
try
{
motorTimer = new Timer();
// timer sets intervals for when quotes are checked after start up
motorTimer .Elapsed += new ElapsedEventHandler(QuoteTimerElapsed);
motorTimer .Interval = Convert.ToDouble(ConfigurationSettings.AppSettings["ServiceCheckInterval"]);
motorTimer .Start();
}
catch (Exception ex)
{
MotorServiceCheckLogDetail(ex.ToString());
OnStop();
}
As an answer to my problem I took the following approach. It might not be perfect and I slightly changed my approach but it was a way I was able to get my Service to work how I wanted.
Instead of creating 1 service with multiple 'services' being run within it, I created one service that instantiated various classes. I was able to include the functionality required in these classes and call the class when required.
As I say, this isn't the perfect answer to my original problem but it was a work around I discovered and as a result it may help others if they read this SO post.
I have already heard/read some problem that people are facing when they are using timer in windows service. Instead of using timer I'd recommend in OnStart method start a task that uses loop with integrated delay. something like following:
Task _task;
CancellationTokenSource _terminator;
protected override void OnStart()
{
_terminator = new CancellationTokenSource();
_task = Task.Factory.StartNew(TaskBody, _terminator.Token);
}
private void TaskBody(object arg)
{
var ct = arg as CancellationToken;
if (ct == null) throw new ArgumentException();
while(!ct.sCancellationRequested)
{
YourOnTimerMethod();
ct.WaitHandle.WaitOne(interval)
}
}
public override void OnClose()
{
_terminator.Cancel();
_task.Wait();
}
I had the same problem. Seems that the OnStart method is called only on the first Service passed to ServiceBase.Run.
I'm fairly new to c# and am in the process of writing a system service. One of the first things that the service needs to do is connect to the internet and download a new settings file. However, as there is no guarantee that the machine it is running on will have an internet connection at startup, the service needs to intermittently attempt to download the file.
The problem I'm having is that by sitting in a loop attempting to download the file the service times out (fails to start).
How can I create a loop that will poll my server intermittently while still allowing the service startup to complete?
UPDATE
I have put together the following code. It appears to work in a non-blocking way, but I cannot work out how to stop the timer from within the netCheck function?
public static void Start()
{
// Start the system timer update
System.Timers.Timer time = new System.Timers.Timer();
time.Interval = 5*1000; // 3hrs
time.Elapsed +=new System.Timers.ElapsedEventHandler(netCheck);
time.Start();
}
public static void netCheck(object sender, EventArgs e)
{
try
{
WebClient webClient = new WebClient();
string download = webClient.DownloadString("http://www.domain.com/ping.php");
if (!string.IsNullOrEmpty(download))
{
//stop the clock
}
else
{
Console.WriteLine("No Net...");
}
}
catch (Exception)
{
Console.WriteLine("No Net...");
}
}
You can just set your service's startup type to Automatic (Delayed Start), as shown below. This will allow your network connection to be running and in place before your service starts.
Or, if it's still possible that you won't have an internet connection, even after a delayed start, have your service start a timer and check for an internet connection in your timer callback.
From https://superuser.com/a/285655:
A service marked as Automatic (Delayed Start) will start shortly after
all other services designated as Automatic have been started.
It's easy. Just set Enable to false
time.Enable=false;