Reputation: 6744
I have a custom Installer class that is used when installing my Windows Service. Stripped down to the necessary details the class looks like this.
[RunInstaller(true)]
public class MyWindowsServiceInstaller : Installer
{
public MyWindowsServiceInstaller()
{
ServiceProcessInstaller processInstaller = new ServiceProcessInstaller();
ServiceInstaller serviceInstaller = new ServiceInstaller();
processInstaller.Account = ServiceAccount.LocalSystem;
serviceInstaller.DisplayName = Program.ServiceDetails.Name;
serviceInstaller.Description = Program.ServiceDetails.Description;
//Must be the same as what was set in Program's constructor
serviceInstaller.ServiceName = Program.ServiceDetails.Name;
Installers.Add(processInstaller);
Installers.Add(serviceInstaller);
}
}
The service is then installed through code by calling the following class, depending on arguments passed into the service, like so. This is called from inside of Main
.
using (ServiceHandler serviceHandler = new ServiceHandler(program.ModuleName, typeof(Program).Assembly))
{
serviceHandler.InstallService();
}
Where the ServiceHandler
class is (again stripped down to remove noise).
public class ServiceHandler : IDisposable
{
private ServiceController _serviceController;
private AssemblyInstaller _assemblyInstaller;
public ServiceHandler(string serviceName, Assembly assembly)
{
_serviceController = new ServiceController(serviceName);
_assemblyInstaller = new AssemblyInstaller(assembly, null);
_assemblyInstaller.UseNewContext = true;
}
public void InstallService()
{
if (IsServiceInstalled())
{
return;
}
IDictionary state = new Hashtable();
try
{
_assemblyInstaller.Install(state);
_assemblyInstaller.Commit(state);
}
catch
{
try
{
_assemblyInstaller.Rollback(state);
}
catch { }
throw;
}
}
public bool IsServiceInstalled()
{
try
{
ServiceControllerStatus status = _serviceController.Status;
}
catch
{
return false;
}
return true;
}
}
However, at the moment all of our services use the same MyWindowsServiceInstaller
but copied into each project separately. To resolve this I was going to move that class to a common assembly with some other functionality (and remove the coupling of the class with Program
) but I'm not sure if it's possible to have the Installer
in another assembly.
Is this possible? If so how do I go about it?
I imagine another problem with my approach is the typeof(Program).Assembly
call to create the ServiceHandler
but I'm not sure.
Upvotes: 2
Views: 543
Reputation: 341
Installer looks into the assembly and looking for [RunInstaller(true)] attribute. Only think you should do: Mark for installer witch is your installer class. Put an inherited empty class into your main assembly.
Common Assembly:
//[RunInstaller(true)] <<-- REMOVE this
public class MyWindowsServiceInstaller : Installer
{
public MyWindowsServiceInstaller(){
ServiceProcessInstaller processInstaller = new
ServiceProcessInstaller();
ServiceInstaller serviceInstaller = new ServiceInstaller();
processInstaller.Account = ServiceAccount.LocalSystem;
serviceInstaller.DisplayName = Program.ServiceDetails.Name;
serviceInstaller.Description = Program.ServiceDetails.Description;
//Must be the same as what was set in Program's constructor
serviceInstaller.ServiceName = Program.ServiceDetails.Name;
Installers.Add(processInstaller);
Installers.Add(serviceInstaller);
}
}
Main assembly
[RunInstaller(true)] // <<-- put it here
public class ProjectInstaller : MyWindowsServiceInstaller { }
Upvotes: 4