Multiple Windows Services in One exe - c#

I am trying to build several Windows services to do different things. For instance, I need Windows services that will:
Send a daily report via email
Periodically cleanup some archived info every 30 minutes
etc.
The tasks I need the windows services to do are distinct so I don't really like the idea of having them all in one service.
What I've got so far is a project in Visual Studio 2008. I've created a windows service, I've set up a timer on the OnStart event (it just writes to a text file every 5 seconds for testing purposes). I then added an Installer to the project and when I run InstallUtil.exe, everything works fine.
The problem comes in when I add a second windows service to the same project. I set up the OnStart code again, with the same logging info (slightly different so I can tell which service is writing to the log). With the second windows service, I changed the Main event in Program.cs from:
static void Main(string[] args)
{
ServiceBase[] ServicesToRun = new ServiceBase[]
{
new Service1()
};
ServiceBase.Run(ServicesToRun);
}
to:
static void Main(string[] args)
{
ServiceBase[] ServicesToRun = new ServiceBase[]
{
new Service1(),
new Service2()
};
ServiceBase.Run(ServicesToRun);
}
At this point, there are no compile time errors, but the Service2 service never does anything...the logging task never fires.
I narrowed it down to the fact that the second service has no "Installer" associated with it. I then tried to add an Installer the way I did with the first service (i.e., right click on the service designer, and click "Add Installer"). Now, when I go to the ProjectInstaller.cs file, there is another serviceInstaller there (serviceInstaller2).
Now when I build the project and try to install the services, and I go to the "Services" control panel window, and I try to start Service1, I get the following error message:
Windows could not start the Service1 service on the Local Computer.
Error 1083: The executable program that this service is configured to run in does not implement the service.
I get the same error message if I try to start Service2 as well (with the exception that the error message identifies Service2, of course).
Is there something I am missing in order to get two services running from one exe?

I have figured out how to have one executable but two services. Each service installs to the service manager with its own name and ability to start/stop. I think this is what you're wanting, correct? Here is what I did:
Created a service project.
Added a second service to the same project (with a unique service name).
Added an installer to both services (ServiceA and ServiceB).
In the ProjectInstaller.Designer.vb file I changed the Me.Installers.AddRange line to show both of the service installers. (Me.ServiceInstaller1, Me.ServiceInstaller2)
In the Main entry point of the main service (ServiceA in my case), I set the ServicesToRun variable to an array of ServiceBase containing all the services that I want it to run (ServiceA and ServiceB). This is an important step as the service manager sets a property based on the number of arguments here - either to allow multiple instances of the same exe or only a single instance.
Add an installer project and use the output of Services.
Add a custom action using the output from Services.
You can find the demo code here: http://code.google.com/p/multi-service-install/
Enjoy!

I guess that you guys have already solved this, but in case that some else needs it, I'll post an answer to this issue which took me several hours today.
The solutions is not to add another project installer, but a service installer, which is a component on the project installer. That newly added service installer has to have second service name configured.

I had a similar problem today, and managed to resolve it.
Firstly, I made sure that each of the services had a meaningful and unique ServiceName property. The generator gave both of the services the same name which would not have helped.
I then removed and regenerated the ProjectInstaller class, and added installers for each of the classes. I made sure they were both instantiated in the static Main method.
I can now install the two services with the one dll, but unfortunately when I start one of the services it seems to perform the functions of both itself and the other service. That is, both of the services are running (even though only one of the services appears as 'Started' in Computer Manager). I'm still trying to figure that one out...

I'm actually building something very similar to what you're thinking of. What I've decided to do (thus far) is have all of my 'services' (though they aren't services, the one 'controller' is) implement a particular interface (with init() and execute() operations as well as a FREQUENCY enumeration).
The controller is the windows service and it reads the list of programs/dlls from an xml settings file at runtime and loads them into a List and calls their execute() method (if applicable) on whatever frequency they have defined.
In my case, each program also contains a usercontrol that is loaded into a tab page of the controller that allows the user to control/modify it. I'll post code if you're interested.
Not sure if that was clear. I actually got the idea from another SO user who implemented something similar but I can't find that post now.

I used sc.exe to accomplish this easily. http://support.microsoft.com/kb/251192
For ex: sc create "My Display Service Name" binpath= "c:\app\mysvc.exe --service"
Note: every equal should have space before its value as you can see for 'binpath'

Installing 2 distinct and unrelated services in one executable has a funny smell to me. While it's possible to get it to work, it doesn't make it right.
From what it sounds like, you need to perform some different tasks at various intervals and your tasks may or may not be related. Have you looked into Quartz.NET? It sounds like a good fit for your needs.
http://quartznet.sourceforge.net/features.html

I had the same problem today and managed to fix it.
In my case I just had to open the *.designer.cs files of my services and make sure that the service name is set correctly. More importantly the same name should be used in the *Installer.Designer.cs and if you have code in *Installer.cs.
I think this error occurs only because of name mismatch.
Hope this helpds

Related

How to test an API service in Visual Studio

How can I use the debug a service within Visual Studio 2010? What I would like to do is step thru the code as the request is being processed.
I am running the service by right clicking on the web site project (C:...\APIServiceSite) within the solution and selecting "View in Browser". Everything runs OK and I get the expected results back from the service but I cannot find a way to step thru the code which is in a another project (APIService) within the same solution.
Off the top of my head: Right click the service project and choose Debug >> Start New Instance. Then start the debug on your web "test" application. You should now be able to accomplish what you desire. (since this is off the top of my head, if it fails, it is very close to the right answer).
A better option is to move the functionality into a class library and make the web service project very thin (essentially returning calls from the class library assembly). You can then test the business functionality by using a unit test library, like MSTest (built in with most team system SKUs of Visual Studio) or nUnit (etc). Sure the tests will likely be integration tests, but you accomplish two good things:
You get the UI out of the testing equation so you can focus on the business logic
You create repeatable tests (think science, not art)
I find it a really bad pattern to have moving parts in a UI project, unless the moving parts are focused on presentation of data. And, yes, I see web services as a UI, even though the user is an application rather than a human body.
I debug a service by making a standard wpf application that uses it. By adding Existing item / Add as link all source file from the service into a new project (wpf application), into a separate folder of this application. So i can test it, and debug the same way i test/debug an application.
Note that you might use the microsoft log service to log issues that your service may have when running as a service. For handling the errors, i made one MustOverload (in vb) function that handle errors, and i overload it as a MessageBox when using the service in my test app, and overload it as ms log when it runs as service
(quick info on log here :
http://msdn.microsoft.com/en-us/library/aa984385(v=VS.71).aspx
)

C# dynamic file system watcher service

I want to be able to develop a windows service which is capable of running multiple instances each with different parameters.
Ideally I want to be able to maintain these parameters in a browser based control panel.
I have written a control panel in C# which saves the config data to an XML file.
From this I want to be able to configure the number of services to run, and what their parameters should be.
I want to be able to dynamically add and remove instances of the service as required.
My questions are:
1) Is this even possible?
2) Can I start a service with specific properties, from the control panel? (Maybe by using "NET START" with command line parameters?
[Edit]
I just saw something online regarding the ServiceController class; can this be used to add and remove instances of a service as well as start/stop services?
[/Edit]
Thanks for any help
Edit: My initial answer was factually wrong.
You can use command line parameters, either with NET START (which however will only accept parameters starting with a backslash) or with SC START (which will accept anything as a parameter).
You cannot start a service with dynamically chosen command line parameters. Parameters can also be specified at service registration time, in which case they remain constant thereafter.
However, starting multiple instances of a service sounds like the wrong idea. There is nothing stopping you from making just one instance of the service that you configure at runtime by communicating with it (e.g. with ServiceController.ExecuteCommand), which is what you should do IMHO.
To communicate with a service, see for example How to communicate with a windows service from an application that interacts with the desktop? and How to create and communicate with a C++ Windows Service in Visual Studio 2010?

c#: How do I run multiple windows services as one service?

I have three windows services in one C# project. Using the installer class (it contains three service installers and one process installer) I was able to install all of my services and start them as three different windows services.
Now I'm trying to run those services under one service name (I would like to see one service name in service control manager, not three). What would be the best approach to do this?
Thanks in advance!
You could just have one main service that spawns off the other executables as regular processes (see Process.Start(..)) which would not show up as windows services. That service would have to control the life time of the dependent processes (start them after the service was started /stop them before shutdown).
Create a new windows service and start 3 thread.
All you have to do is implement some principles of code reuse.
Move each of the respective work units into classes/libraries rather than services. Create a single service referencing the end result libraries. Your service intention is likely a timer of some sort, and possibly exposing API endpoints. Create the single service and activate the necessary classes from 3 unique timers (if needed - you might get away with a single timer activating 3 different class methods). Wrap events or API endpoints in a similar fashion. A services should contain little or no "business" logic, it should derive that work from referenced business libraries. Follow that practice and your question will mostly answer it's self.

.NET App.Config issues across multiple projects

I have a solution that contains two projects. One project is a Windows Service that contains a few classes for performing various functions. The other project is a Windows Form Application that is meant for doing various testing and performing task that the service accomplishes on demand. For instance, the service will do a few various task at a certain time interval, but if you wanted to perform one of those task before the time interval has elapsed, you could load the Windows Forms Application and execute one of the task immediately. The Windows Form Application (from here referred to as the tester) references the Windows Service (from here referred to as Service) project. When the Tester loads, it creates an instance of one of the classes from the Service. Whenever buttons are pressed to perform the task, it references that instance that was created and calls the various methods of the class. The Service has uses application settings for the configuration, and thus has an app.config. The configuration settings are accessed using [ProjectName].Properties.Settings.Default.[SettingName]. When the configuration settings for the tester are changed on its own app.config (or exe.config after release), the settings are refreshed without a problem. If the tester is closed, configuration is modified, and then reopened, the new settings will be populated. If the Reload method is called on the Settings, the configuration is refreshed without a problem. The Service performs in the same way. The configuration settings are can be updated without a problem.
The problem is that if configuration changes need to be made on the service, when the service methods are called from the tester, the configuration does not take the new settings. Whatever settings the project was built with will be used. I cannot get the tester to recognize that the app.config/exe.config for the service has changed, and the new configuration should be reloaded and used instead of whatever configuration was used when the project was built in visual studio. Perhaps it I am not educated enough about this topic, but it seems to me that the app.config is rolled up into the .exe file created by the Service whenever it is built.
I have tried encapsulating the Reload method for the Service and calling it, and it doesn't seem to make a difference.
I have an example project that performs as is described above on a very basic level. The ideal goal is to be able to modify the exe.config file or files on the fly and have both the Service and the Tester pick them up without any problems. If you would like to see exactly what I mean from a coding perspective, an example project can be found here: example solution
Any help would be greatly appreciated. Thanks you for your time.
Update
I have managed to find A solution to this, but I am still curious if there is a better way. By using an app.config that is shared between both projects and using the Configuration Manager instead of App Settings, everything works as it is expected to. I prefer to use App Settings if at all possible; however it will work to use a shared app.config and the Configuration Manager pointed at that app.config.
One option is having both programs load from the same configuration file:
http://msdn.microsoft.com/en-us/library/system.configuration.configurationmanager.openexeconfiguration.aspx
Found the solution. An app.config must be added to the solution as is described above in my update. Once linked like that, you can add a settings file to the project and add settings/configuration to the project and they will be added to the solutions settings file. This will move with the build and fix the problem I was having.

How do I configure the name of a Windows service upon installation (or easily at compile time)?

I've created a Windows service in C#, installed it on a server and it is running fine.
Now I want to install the same service again, but running from a different working directory, having a different config file etc. Thus, I would like to have two (or more) instances of the same service running simultaneously.
Initially, this isn't possible since the installer will complain that there's already a service with the given name installed.
I can overcome this by changing my code, setting the ServiceBase.ServiceName property to a new value, then recompiling and running InstallUtil.exe again. However, I would much prefer if I could set the service name at install-time, i.e. ideally I would do something like
InstallUtil.exe /i
/servicename="MyService Instance 2"
MyService.exe
If this isn't achievable (I very much doubt it), I would like to be able to inject the service name when I build the service. I thought it might be possible to use some sort of build event, use a clever msbuild or nant trick or something along those lines, but I haven't got a clue.
Any suggestions would be greatly appreciated.
Thank you for your time.
I tried accessing a configuration using
ConfigurationManager.OpenExeConfiguration(string exePath)
in the installer, but couldn't get it to work.
Instead I decided to use System.Environment.GetCommandLineArgs() in the installer like this:
string[] commandlineArgs = Environment.GetCommandLineArgs();
string servicename;
string servicedisplayname;
ParseServiceNameSwitches(
commandlineArgs,
out servicename,
out servicedisplayname);
serviceInstaller.ServiceName = servicename;
serviceInstaller.DisplayName = servicedisplayname;
Now I can install my services using
InstallUtil.exe /i
InstallableService.dll
/servicename="myserviceinstance_2"
/servicedisplayname="My Service
Instance 2"
I wrote up a more elaborate explanation here.
You can't pass this in as a command line arg, since InstallUtil doesn't provide the right hooks for that.
However, you can make your service installer read the ServiceName from a config file. If you look at some code for a typical ServiceInstaller, you'll see it's just a matter of having the appropriate DisplayName and ServiceName properties setup at runtime. These could easily be read from a configuration file instead of being hard-coded.
Instead of using Environment.GetCommandLineArgs(); the class Installer has a property called Context from which you can access command line arguments passed to InstallUtil structured in a nice StringDictionary.

Categories