Add commandline arguments to service installer c# - c#

I am working on an application which starts as a service but only if a commandline switch tells it to (otherwise a standard form is opened). So when the service is started by Windows at bootup, it must pass this commandline option or the service fails to start.
I would like to have the installer (ServiceProcessInstaller) add a commandline option so that when the service is started it adds the commandline option to the command.
Example:
MyService.exe -commandlineoption
I thought this was what the ServiceProcessorInstaller.Context property was for, but that is for the arguments that were executed on InstallUtil.
Any suggestions?

When I've added command-line options to services, I've always defaulted to running as a service. However, I know that the opposite is possible because it's how SvcHost works: it's an EXE that's always configured to load DLL's as services.

A service is only installed once per release. It sounds like you are talking about passing a command line argument to the service when it's started.
You can pass command line arguments to the service when you start it using the ServiceController.Start method:
using (var controller = new ServiceController("servicename")) {
controller.Start(arg0, arg1);
}

Related

Finding which service has run an executable in C#

I was wondering if it were possible to find out which windows service has run an executable?
I've got two different services running from the same exe, doing different things. The main method of the program detects a command line parameter and will either start the console app (if running in Environment.UserInteractive), or start one of the two possible services. I can't find a good way to discern which service is running so I can choose the correct service to start in the code. Passing in .exe parameters in the image path of the windows service doesn't seem to work. The services are running on a server and starting automatically, so doing it manually isn't really an option.
I'd really like to avoid having to have two different projects with different executables, so any way I can notify the program of which service to run would be great.
You can pass arguments in the ImagePath. I know I have done it at some point using a .net windows service, but as I recall, I had to install the service using something other than the standard .Net installer.
The .Net installer adds quotes around whatever you pass, which makes ImagePath go from C:\test\test.exe -arguments to "C:\test\test.exe -arguments" when it should be "C:\test\test.exe" -arguments.
Check out WiX, sc.exe or CreateService to get the correct registry value.
To test, install your service as usual and browse in regedit to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\yourServiceName and edit ImagePath. For an example of how it should look, check out HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\seclogon.
You could create a Mutex in your service at the point it starts, using a name unique to the version it is running. If the mutex is obtained then you know it isn't running. If it can't be obtained then the service is already running.
You could then start your services through a new process that first tries to obtain the mutex for the first service and if it can't obtain it it starts the second.
So, you start ServiceRunner.exe -foo. A mutex called "foo" is obtained, so you release the Mutex and ServiceRunner.exe starts Service.exe -foo.
If the mutex is not obtained you then try to obtain a mutex called "bar" and follow the same process.
This is a nasty solution, and would require your to create a new exe that simply tries to start the services.
Have your service share its start state( temp file, registry key or other method, it could even write this as html to a web server... )
ServiceController yourService = new ServiceController( "YourServiceName" , "YourMachine" );
if( yourService.Status == ServiceControllerStatus.Stopped )
{
yourService.Start();
}

Passing a parameter to a service installer via installutil

I'm trying to write a power shell script to install a service but the service requires an extra command line paramiter passed to it. Im having trouble getting this passed over.
Here is the service installer that uses the parameter;
this.serviceInstaller.ServiceName = string.Format("My brill service {0}",this.Context.Parameters["environment"])
And I have tried passing the paramiter in two ways;
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe "C:\foo\bar.exe /environment:tomtest"
(this gives the error "invalid directory on url")
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe /environment:tomtest "C:\foo\bar.exe"
(this just dosent change the service name)
Any ideas? Thanks
I was very close. An equals sign = has to be used to assign the value of the parameter (not a colon :):
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe /environment=tomtest "C:\foo\bar.exe"

Automatically restart a self hosted wcf service on crash

I have a self hosted wcf service that is using legacy c++ code via dll import and pinvoke. There are some instances in the old code where exceptions arise from the functions (that were handled in the old app, but not in the service) and when they occur my service is stopping. The exceptions are rare; however, I do not want my service just randomly stopping as a result of a crash in another assembly. The exceptions are not bubbling up to the service so I cannot try/catch them in the service. Is there a way to automatically have the service restart on crash?
It is self hosted, not through IIS.
Thanks in advance!!
On the machine where the service is running, you can open up the Services management console (start > run > services.msc). Find your service, right-click it and choose Properties. In the popup, click on the Recovery tab. Set First, Second, and Subsequent failures all to Restart the Service.
If you are using WIX to install your project, you can also set these properties using the util:ServiceConfig element.
If you're using a standard ServiceInstaller, these options aren't built in. I'd recommend having a look at the ServiceInstaller Extension class which exposes properties through a standard service installer interface.
Well I'm assuming that you are creating a Windows Service project for what you are doing. Go into your ProjectInstaller and find the "AfterInstall" method. Here you will need to add code to execute a command on the Service Controller to set recovery options. Unfortunatly, even though .NET has a ServiceController you will need to execute the command through a process start.
using (var process = new Process())
{
var startInfo = process.StartInfo;
startInfo.FileName = "sc";
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
// tell Windows that the service should restart if it fails
startInfo.Arguments = string.Format("failure \"{0}\" reset= 0 actions= restart/60000", serviceName);
process.Start();
process.WaitForExit();
exitCode = process.ExitCode;
process.Close();
}
Note: I stole this code from another question

How do I get the command-line arguments of a Windows service?

I'm looking for a way to figure out the command-line arguments of any Windows service.
For a non-service process, the command-line arguments can be found in the Windows Task Manager, or programmatically by using WMI as shown in this post.
Unfortunately, these two solutions don't work for a Windows service that is started by the ServiceController.Start(String[] args) method. Both of them show only the executable file path on the command-line, even though some arguments were passed in.
What is the difference
between two scenarios (a service vs.
a non-service process)?
Is there a
way to figure out the arguments of the
Windows service?
I also tried creating a simple service that just logs any command-line arguments it has to the event log. I started it using "sc.exe start <my service> <arg1>" and verified that <arg1> was written to the event log.
However, none of the solutions has worked for me. I still only saw the path to the executable file. My OS version is Windows Server 2008 R2 SP1 x64 Enterprise.
There are two types of arguments for services:
Arguments that were passed on the process start command line. You can get to those easily using Process Explorer, etc.
Arguments that were passed to the ServiceMain function. This is the WIndows API that a service is supposed to implement. The .NET equivalent is ServiceBase.OnStart. This is what is used when you do an SC START \[arguments\]. This has nothing to do with "command line process arguments".
The second type of parameters is probably only known by the service itself, if the implementation makes any use of it which is not the case for many services. I don't think Windows keep track of this when we look at low level Windows structures like the PEB: Process and Thread Structures (MSDN), even the undocumented parts of it, Undocumented functions of NTDLL.
You can find the service EXE file details and edit or just see the commandline options in the registry entry for the service. You'll find that under
HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\services
Be sure to restart the Services window if you decide to change this as it won't reread it live.
Try the Process Explorer application from Sysinternals
It is like Task Manager, only it lists all the running processes. Select your service and see its properties.
A service process is not started as a usual EXE file. Even more, a service process could be just a .dll file. See: Windows service (Wikipedia).
Many appear in the processes list in the Windows Task Manager, most often with a username of SYSTEM, LOCAL SERVICE or NETWORK SERVICE, though not all processes with the SYSTEM username are services. The remaining services run through svchost.exe as DLLs loaded into memory.
Just override the ServiceBase.OnStart(string[] args) method. See more:
ServiceBase.OnStart(String[]) Method (MSDN)
Using Powershell you can call
(Get-CimInstance Win32_Service -Filter 'Name = "<my service>"').PathName
to get the full command line of the service (it returns file and arguments)
Just replace <my service> with the name of the desired service.
For example:
(Get-CimInstance Win32_Service -Filter 'Name = "Dnscache"').PathName
returns "C:\WINDOWS\system32\svchost.exe -k NetworkService -p"

What is the accepted pattern for an application that can be run as a service or as a console application

I have a project that is deployed to production as a windows service. However for local development purposes it would be useful to run it as a console application. At the moment I have a class Called ReportingHost that provides my core functionality, And a class called ReportingServiceHost that inherits from ServiceBase and allows me to run the application as a service. There is also a program class with a main method that calls ServiceBase.Run on my ReportingServiceHost.
I think I need to write a ReportingConsoleHost class that allows me to run the functionality in a console. Then I need to modify my Main to react to a command line switch and choose one or the other. These are the two bits I am having trouble with.
I have had a look at this and attempted to use that code but my app exits immediately, it doesn't show a console window and it doesn't wait for Enter before closing.
Part of the problem is that I dont have a deep understanding of how these things work. a definitive pattern for splitting my functionality, my two different ways of running that functionality, and a main method that chooses one of these ways based on a command line argument is what I am hoping to achieve.
I suspect your test project was configured as a windows exe, not a console exe. With a windows exe Console.ReadLine will return immediately.
To have a console exe that works both as a service and at the command line, start it as a service project (in Visual Studio) - and add a check on Environment.UserInteractive - i.e.
static void Main() {
if(Environment.UserInteractive) {
// code that starts the listener and waits on ReadLine
} else {
// run the service code that the VS template injected
}
}
You can of course also use a command line switch. I have example on microsoft.public.dotnet.languages.csharp that acts as:
an installer / uninstaller
a service
a console-mode app
depending on the switches
I have done this before by implementing a normal Windows Service (by deriving from ServiceBase), but putting a check in the main method to check for a command line argument.
If the args contain /console, start the console version, otherwise start the service.
Something like this:
internal class MyService : ServiceBase
{
internal static void Main(string[] args)
{
if (args.Length == 0)
{
// run as a service....
ServiceBase[] servicesToRun = new ServiceBase[] {new MyService()};
Run(servicesToRun);
}
else
{
// run as a console application....
}
}
}
My advise? Put all your logic for your service in a separate assembly. (A class library or DLL.) Then create one project as service which references your class library and puts the code to use as services. Create a second console project which also references your class library but which will make it available as a console application.
You would end up with three different projects in your solution but it does allow you to keep things separate. Actually, this would make it possible to extend your service in several other shapes too. You could, for example, create a 4th project as a web service and thus call your service from a web browser on a client system. Because the software logic is separated from the usage logic, you gain lots of control over it.
Be aware that a service will possibly run with more limitations than a console application. In general, services don't have network access by default, don't have a monitor assigned to them to display error messages and in general run with a limited user account or system account. Your service might work as a console yet fail as a service because of this.
There are already two good answers above - but I thought I'd post a link to Brian Noyes' Debuggable Self-Host Windows Service Project blog post - it talks about WCF but should apply to any 'Windows Service'.
The best thing is the sample code - if you can't figure out where the above examples 'fit', grab the complete project and see how it works. Thanks Brian!

Categories

Resources