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....
Related
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()
I am trying to learn about writing Windows Services, and I'm using C# to do it. I found a tutorial on Microsoft's site here:
https://msdn.microsoft.com/en-us/library/zt39148a%28v=vs.110%29.aspx
It shows you how to build a simple Windows Service that logs some messages to the Event Log. I more or less followed the instructions, and everything worked fine. But it instructs you to use Designers and mysterious drag-and-drop components, and to rely upon a bunch of IDE-autogenerated code. So, for the purposes of trying to actually understand what I was doing, I tried to make a (basically) equivalent Windows Service just from raw classes that I manually typed in, rather than from Designers and such.
It compiled fine, and installutil.exe successfully installed it as a service. But when I try to start the service, I get the following error:
Windows could not start the Bob Manual Service Display Name service on Local Computer.
Error 1053: The service did not respond to the start or control request in a timely fashion.
Eventually I figured out that Malwarebytes (my antivirus program) is quarantining my project's compiled exe whenever I try to start the service. It says it's infected with "Backdoor.Bot". I have tried both debug and release builds, and it quarantines both.
Obviously I could just whitelist the exe or temporarily disable Malwarebytes or whatever, but I am completely new to this Windows Service stuff, and I am concerned that I am perhaps unknowingly doing something flagrantly wrong or dangerous in my code.
I am attaching my code, which is intended to be based on the Microsoft sample code but very simple - three small classes, a Main, a System.ServiceProcess.ServiceBase, and a System.Configuration.Install.Installer.
Is there any reason why Malwarebytes should be quarantining this?
One thing that I imagine might look questionable is that I'm setting the account to ServiceAccount.LocalSystem, which that Microsoft tutorial says has "broad permissions" and so "might increase your risk of attacks from malicious software", but:
(1) That's what it is in Microsoft's sample code (and as I understand it, it's required for the EventLog stuff);
(2) I actually accidentally had it as LocalService at first, and the same error was happening.
namespace Project1
{
using System.ServiceProcess;
static class BobMain
{
static void Main()
{
ServiceBase[] ServicesToRun = { new BobManualService() };
ServiceBase.Run(ServicesToRun);
}
}
}
namespace Project1
{
using System.Diagnostics;
using System.ServiceProcess;
public class BobManualService : ServiceBase
{
private EventLog eventLog;
public BobManualService()
{
this.eventLog = new EventLog();
if (!EventLog.SourceExists("BobSource"))
{
EventLog.CreateEventSource("BobSource", "BobLog");
}
this.eventLog.WriteEntry("Super duper constructor!");
}
protected override void OnStart(string[] args)
{
}
protected override void OnStop()
{
}
}
}
namespace Project1
{
using System.ComponentModel;
using System.Configuration.Install;
using System.ServiceProcess;
[RunInstaller(true)]
public class BobInstaller : Installer
{
private ServiceProcessInstaller serviceProcessInstaller;
private ServiceInstaller serviceInstaller;
public BobInstaller()
{
this.serviceProcessInstaller = new ServiceProcessInstaller();
this.serviceInstaller = new ServiceInstaller();
this.serviceProcessInstaller.Account = ServiceAccount.LocalSystem;
this.serviceProcessInstaller.Password = null;
this.serviceProcessInstaller.Username = null;
this.serviceProcessInstaller.AfterInstall +=
this.serviceProcessInstaller_AfterInstall;
this.serviceInstaller.Description =
"Bob Manual Service Description";
this.serviceInstaller.DisplayName =
"Bob Manual Service Display Name";
this.serviceInstaller.ServiceName = "BobManualService";
this.serviceInstaller.StartType = ServiceStartMode.Automatic;
this.Installers.AddRange(new Installer[]
{
this.serviceProcessInstaller,
this.serviceInstaller
});
}
private void serviceProcessInstaller_AfterInstall(
object sender,
InstallEventArgs e)
{
}
}
}
Looking at your code, in the OnStart(string[] args) method, there is no process being started that runs on a separate thread for a prolonged time. So your service starts and exists immediately. Please take a closer look at "To define what occurs when the service starts" in the tutorial that you've referenced.
Basically just add the code that sets up the timer to the OnStart(string[] args) method and your service should stay alive.
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 am trying to update a C# application published by VS2010 on IIS7.5.
I need to uninstall it and then install a new one.
If I ran the code in debug mode, it worked well.
But, if I ran it in release without debug mode. I got error of
Another version of the product is already installed. Installation of this version cannot continue. To configure or remove the existing version of this product, use Add/remove programs on the control panel.
I found the reason is that "install" started when "uninstall" is still not finished. How to make sure that the "uninstall" is finished before "install" started ? I used WaitForExit() (https://msdn.microsoft.com/en-us/library/ty0d8k56.aspx) but, after uninstall, the "install" was not executed.
The code is here:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace msiexec_uninstall_install
{
class Program
{
static void Main(string[] args)
{
var p = System.Diagnostics.Process.Start("msiexec", "/uninstall http://MyServer/MyApp.msi");
p.WaitForExit();
Console.WriteLine("after uninstall");
System.Diagnostics.Process.Start("msiexec", "/i http://myServer/MyApp_new.msi");
Console.WriteLine("after install");
Console.ReadLine();
}
}
}
If your installation takes longer than it takes to finish your Main program, you won't see it.
Try adding WaitForExit(); for your installation as well.
static void Main(string[] args)
{
var uninstallProcess = System.Diagnostics.Process.Start("msiexec", "/uninstall http://MyServer/MyApp.msi");
uninstallProcess.WaitForExit();
Console.WriteLine("after uninstall");
var installProcess = System.Diagnostics.Process.Start("msiexec", "/i http://myServer/MyApp_new.msi");
installProcess.WaitForExit();
Console.WriteLine("after install");
Console.ReadLine();
}
That way your Main method will only end after installation is complete. To be on the safe side, you can even add some timeout for WaitForExit():
installProcess.WaitForExit(10000); //10 sec
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.