 |
|
 |
Based on this great article, and the very useful added service template I'd like to share what I've experienced while searching for a way to install two services from the same assembly:
This might be a bit tricky, but the way to differentiate which service is actually being started is the Service's status:
if (new ServiceController("Service1").Status == ServiceControllerStatus.StartPending)
ServiceBase.Run(new Service1());
if (new ServiceController("Service2").Status == ServiceControllerStatus.StartPending)
ServiceBase.Run(new Service2());
When you decide to launch using the override for ServiceBase.Run, as a matter of fact only the first one is actually started.
StartPending is the state assigned to the service immediately after having selected 'Start' from the Services menu.
Best regards,
Piet
|
|
|
|
 |
|
 |
Based off of this excellent article and the many additions made in the comments, I created a 'template' windows service file, that I thought might be useful for others. The code assumes that you have setup the default assembly attributes, and have some type of 'bootstrap' class that has a default constructor and implements IDisposable. Most of the configuration is contained in the WindowsServiceConfiguration. Thanks again for the great article.
To use it, just create a Console Project, replace the Program.cs file with this code, setup the attributes in the AssemblyInfo.cs file, and point the THREAD field to a class to run.
You will need to add references to System.Configuration, System.Configuration.Install, and System.ServiceProcess.
#region Imports
using System;
using System.ComponentModel;
using System.Configuration.Install;
using System.IO;
using System.Linq;
using System.Reflection;
using System.ServiceProcess;
#endregion
namespace MyNamespace
{
public static class WindowsServiceConfiguration
{
#region Constants
public static readonly ServiceAccount SERVICE_ACCOUNT = ServiceAccount.LocalSystem;
public static readonly String SERVICE_DESCRIPTION = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof (AssemblyDescriptionAttribute), true).Cast<AssemblyDescriptionAttribute>().First().Description;
public static readonly String SERVICE_NAME = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof (AssemblyTitleAttribute), true).Cast<AssemblyTitleAttribute>().First().Title;
public static readonly ServiceStartMode START_MODE = ServiceStartMode.Automatic;
public static readonly Type THREAD = typeof (Container);
#endregion
#region User Account Info
public static readonly String SERVICE_DOMAIN;
public static readonly String SERVICE_PASSWORD;
public static readonly String SERVICE_USERNAME;
#endregion
}
public class Program
{
#region Constants
public static readonly Assembly ASM = Assembly.GetExecutingAssembly();
public static readonly String NL = Environment.NewLine;
public static readonly String SERVICE_ASSEMBLY_VERSION = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof (AssemblyFileVersionAttribute), true).Cast<AssemblyFileVersionAttribute>().First().Version;
public static readonly String SERVICE_COPYRIGHT = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof (AssemblyCopyrightAttribute), true).Cast<AssemblyCopyrightAttribute>().First().Copyright;
public static readonly String SERVICE_VERSION = Assembly.GetExecutingAssembly().GetName().Version.ToString();
#endregion
public static int Main(string[] args)
{
if (Environment.UserInteractive)
{
Console.Write(
NL + WindowsServiceConfiguration.SERVICE_NAME + " " +
(SERVICE_VERSION == SERVICE_ASSEMBLY_VERSION ? SERVICE_VERSION : SERVICE_VERSION + " " + "[" + SERVICE_ASSEMBLY_VERSION + "]") +
NL + SERVICE_COPYRIGHT);
if (args != null && args.Length >= 1)
{
if (args[0].ToLower() == "/i") return MainInstaller(true);
if (args[0].ToLower() == "/u") return MainInstaller(false);
if (args[0].ToLower() == "/c") return LaunchConsole();
}
Console.WriteLine(
NL + NL + "Usage: " + Path.GetFileName(ASM.Location) + " [/i | /u | /c]" +
NL + NL + "Where:" +
NL + "\t/i - install service." +
NL + "\t/u - uninstall service." +
NL + "\t/c - run in console.");
}
else
{
ServiceBase.Run(new WindowsService());
}
return 0;
}
private static int MainInstaller(bool shouldInstall)
{
String[] args;
if (shouldInstall)
{
Console.WriteLine();
args = new[] {"/LogFile=", ASM.Location};
}
else
{
args = new[] {"/u", "/LogFile=", ASM.Location};
}
try
{
ManagedInstallerClass.InstallHelper(args);
return 0;
}
catch (Exception e)
{
Console.WriteLine(" --- ERROR --- " + NL + e);
return -1;
}
}
public static int LaunchConsole()
{
Console.WriteLine(NL + NL + "Starting... Press 'Q' or 'Esc' to exit");
try
{
using (CreateThread())
{
while (true)
{
var key = Console.ReadKey(true).Key;
if (key == ConsoleKey.Q) break;
if (key == ConsoleKey.Escape) break;
}
Console.WriteLine(NL + "Exiting" + NL);
}
return 0;
}
catch (Exception e)
{
Console.WriteLine(" --- ERROR --- " + NL + e);
return -1;
}
}
public static IDisposable CreateThread()
{
return (IDisposable) Activator.CreateInstance(WindowsServiceConfiguration.THREAD);
}
}
public class WindowsService : ServiceBase
{
#region Constructors
public WindowsService()
{
ServiceName = WindowsServiceConfiguration.SERVICE_NAME;
}
#endregion
#region Fields
private IDisposable thread;
#endregion
#region Service Methods
protected override void OnStart(string[] args)
{
if (thread != null) return;
thread = Program.CreateThread();
}
protected override void OnStop()
{
if (thread != null)
{
thread.Dispose();
thread = null;
}
}
#endregion
}
[RunInstaller(true)]
public class WindowsServiceInstaller : Installer
{
#region Constructors
public WindowsServiceInstaller()
{
if (WindowsServiceConfiguration.SERVICE_USERNAME == null)
{
processInstaller.Account = WindowsServiceConfiguration.SERVICE_ACCOUNT;
}
else
{
if (WindowsServiceConfiguration.SERVICE_DOMAIN == null)
{
processInstaller.Username = WindowsServiceConfiguration.SERVICE_USERNAME;
}
else
{
processInstaller.Username = WindowsServiceConfiguration.SERVICE_DOMAIN + "\\" + WindowsServiceConfiguration.SERVICE_USERNAME;
}
processInstaller.Password = WindowsServiceConfiguration.SERVICE_PASSWORD;
}
serviceInstaller.ServiceName = WindowsServiceConfiguration.SERVICE_NAME;
serviceInstaller.Description = WindowsServiceConfiguration.SERVICE_DESCRIPTION;
serviceInstaller.DisplayName = WindowsServiceConfiguration.SERVICE_NAME;
serviceInstaller.StartType = WindowsServiceConfiguration.START_MODE;
var installer = new Installer[] {processInstaller, serviceInstaller};
Installers.AddRange(installer);
}
#endregion
#region Fields
private readonly ServiceProcessInstaller processInstaller = new ServiceProcessInstaller();
private readonly ServiceInstaller serviceInstaller = new ServiceInstaller();
#endregion
}
}
|
|
|
|
 |
|
 |
The code as is will do nothing in console mode. Would you provide an example where the OnStart event is triggered (or pseudo triggered) in console mode? Assume, please, no IOC container.
Thanks
|
|
|
|
 |
|
 |
The OnStart method calls the same CreateThread method as the console mode, so triggering OnStart would do (as intended) the exact same as the Console mode.
Replace the Program.CreateThread() method to return a timer or some other thread instead. If you need information on threads, check out something like System.Timers.Timer
Here is an example (I haven't tested, just going off the top of my head).
public static IDisposable CreateThread()
{
var timer = new System.Timers.Timer(5000) {AutoReset = true};
timer.Elapsed += ((sender, e) => Console.WriteLine("Hello World: " + DateTime.Now));
timer.Start();
return timer;
}
|
|
|
|
 |
|
 |
When I try to start the service I am getting Error 1053: the service did not respond to the start or control request in a timely fashion. Using an ordinary installer the service works fine.
Any ideas?
|
|
|
|
 |
|
 |
I am creating a deployment app to auto deploy many msi's
currently we use WIX msi's and the installutil to install the services
what i was trying to do was use the ManagedInstallerClass to install the services
problem is how can i retrieve the service's names passing the exe path?
i need to store and restore the service state
public static bool InstallService(string _exePath, string svcName)
{
try
{
if (IsServiceInstalled(svcName)) return false;
ManagedInstallerClass.InstallHelper(new string[] { _exePath });
if (!IsServiceInstalled(svcName)) return true;
}
catch(Exception ex)
{
Logger.Log(ex);
}
return false;
}
|
|
|
|
 |
|
 |
When i install my service using the installUtil.exe i dont get any error and service runs successfully. but when i implement the above code in my service project i get following error message.
Error 1053: The service did not respond to the start or control request in a timely fashion.
Please help
|
|
|
|
 |
|
 |
case "console":
case "c":
MyConsoleHost.Launch();
break;
}
}
else
MyWinServiceHost.Launch();
|
|
|
|
 |
|
 |
hi all,
i am reviewing this code for 3 to 4 days continously but didnt get an idea that
1. what do you mean by service executable. Should i put my below code in windows service project
using System.Reflection;
using System.Configuration.Install;
namespace gotnet.biz.Utilities.WindowsServices
{
public static class SelfInstaller
{
private static readonly string _exePath =
Assembly.GetExecutingAssembly().Location;
public static bool InstallMe()
{
try
{
ManagedInstallerClass.InstallHelper(
new string[] { _exePath } );
}
catch
{
return false;
}
return true;
}
public static bool UninstallMe()
{
try
{
ManagedInstallerClass.InstallHelper(
new string[] { "/u", _exePath } );
}
catch
{
return false;
}
return true;
}
}
}
2. The main() code is given later in this article, again should i put this in my windows service project
if i put that in service project then should i delete the prevoius code
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new WindowsService1.Service1()
};
ServiceBase.Run(ServicesToRun);
plz help me i very upset
|
|
|
|
 |
|
 |
I had my own solution back in 2005 for this: Self installing .NET service using the Win32 API[^]
It uses the native API and the latest versions (available through CodePlex as part of a greater project: http://www.codeplex.com/aspnetSuite[^]) support UAC and 32/64 bit support on Windows, Vista, and beyond. It will auto-detect if it's not installed and attempt to install it. You can also provide command line options to install, uninstall, etc. It also provides install/uninstall/start/stop/etc. utils for any service on the system (each w/ UAC support, if needed) and will automatically degrade to a typical console-based application if running in user interactive mode. It also provides more control than the .net framework-provided one in terms of what messages you can respond to (e.g. system shutdown, power states, etc.), your error handling capabilities, etc.
You can write an entire service w/ all of that functionality w/ ~2 methods.
|
|
|
|
 |
|
 |
After trying your suggestions inside a windows service I'm programming, there is no console output. Surely it has something to do with the type of the project (window service instead of console application), but I don't know what to change in the project properties to get something. Of course, if I start with a console appl I get no service
pd.- by console output I mean everything that gets out from system.console.writeline(), for example.
Thanks in advance for any suggestion...
|
|
|
|
 |
|
 |
Royally late but yes, it does have to do with the project type. If you go to Properties, Application and change the Output type to 'Console Application' you'll get the output you're missing.
Reverse Alchemy - http://www.reversealchemy.net
|
|
|
|
 |
|
 |
I combined the enhancements by Aleksei Nickolayev and Ashley van Gerven, and now have my services accepting command line switches and offering to install or uninstall if launched interactively. This is the best! No more batch files!
Thanks for a great article, and thanks to the other users for the great enhancement ideas!
How could anyone not vote 5?
|
|
|
|
 |
|
 |
I have a problem I can install a Windows service by InstallUtil but I can't do it when I try to do it with ManagerInstallerClass. It's strange, I reflected the Code (is the same that InstallUtil) and it doesn't work. Context data? This is My Code:
static void Main(string[] args)
{
string path = @"";
MainK(new string[] { path });
}
public static int MainK(string[] args)
{
Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentUICulture.GetConsoleFallbackUICulture();
if (((Console.OutputEncoding.CodePage != 0xfde9) && (Console.OutputEncoding.CodePage != Thread.CurrentThread.CurrentUICulture.TextInfo.OEMCodePage)) && (Console.OutputEncoding.CodePage != Thread.CurrentThread.CurrentUICulture.TextInfo.ANSICodePage))
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
}
Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), true);
try
{
ManagedInstallerClass.InstallHelper(args);
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
return -1;
}
return 0;
}
|
|
|
|
 |
|
 |
Awesome post, thanks. Always wanted to know how that was done.
|
|
|
|
 |
|
 |
Hi,
Excellent article! I have found non-command ways to do all things service related, apart from uninstalling...
I have created a helper service (Service1Updater) for my main service (Service1). Service1Updater does two things
-Checks a network folder to see if there's any updates for Service1 and installs them
-Checks to see if Service1 is running, if not then it tries to start it
The reason I'm using a service to do the updating:
-I don't want Service1 to fail when updating, I don't want to manually fix things
-I don't want any user interaction during updates
-I have a large cluster of computers to roll out the updates, I can't do it manually would take ages
-I want Service1 to stay alive if something unforeseen happens, so I use a helper process
The problem:
I use Service1Updater to copy new files -> stop Service1 -> uninstall Service1 -> delete old Service1 files -> copy new files -> install Service1 -> set Service1 properties -> start Service1.
Between "uninstall Service1 -> delete old Service1 files" I always get Access denied or UnauthorizedAccessException as the InstallHelper seams to still have a handle open on the old Service1. If I use a Process command then I have no problem deleteing the old files:
Process myProcess = new Process();
string path = @"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727";
myProcess.StartInfo.FileName = path + "\\InstallUtil.exe";
myProcess.StartInfo.Arguments = @"/u C:\Service1\Service1.exe";
myProcess.StartInfo.CreateNoWindow = true;
myProcess.Start();
myProcess.WaitForExit(60000);
if (!myProcess.HasExited)
myProcess.Kill();
myProcess.Close();
Although this works it would be nice to have an InstallHelper solution especially as InstallUtil maybe in a different location. Does anyone have any ideas? Would I have to host the InstallHelper in a different thread/process and wait for it to exit?
Cheers
Ross
not suitable for idiots
|
|
|
|
 |
|
 |
Hi Ross,
I have the same problem and didn't find any solution for this
Regards
Jürgen
|
|
|
|
 |
|
 |
Hi Guys,
I ran into this problem and found a solution by running the uninstall method in another AppDomain similar to the way it is documented here.
http://www.devnewsgroups.net/group/microsoft.public.dotnet.framework/topic48232.aspx[^]
Not only does it put a lock on the file when you uninstall it, also, if you check to see if it is a installable service (using AssemblyInstaller.CheckIfInstallable) then you cannot delete the file either unless you use this method.
Enjoy...
|
|
|
|
 |
|
|
 |
|
 |
Hi, would you mind providing an example of how you would use the command line switches to set properties of the underlying ServiceProcessInstaller object.
Thanx...
|
|
|
|
 |
|
 |
First of all, THANK YOU for your article. It is really useful.
I have one little improvement. In case of your code user must know what switches service executable accepts. I suppose it's a good idea to show all acceptable switches to user when the service was started from console, not from SCM.
The method that I use to determine how service was started is quite simple — checking the value of Environment.UserInteractive.
Code:
static void Main(string[] args)
{
if (Environment.UserInteractive)
{
Console.Write(Environment.NewLine + System.Reflection.Assembly.GetExecutingAssembly().FullName + ".");
if (args != null && args.Length >= 1)
{
if (args[0].ToLower() == "/i")
{
Console.WriteLine();
installMyService();
return;
}
if (args[0].ToLower() == "/u")
{
uninstallMyService();
return;
}
}
Console.WriteLine(Environment.NewLine + Environment.NewLine + "Usage: MyService.exe [/i | /u]" + Environment.NewLine + Environment.NewLine + "Where:");
Console.WriteLine(" /i - install service;");
Console.WriteLine(" /u - uninstall service.");
}
else
{
ServiceBase.Run(new MyService());
}
}
static void installMyService()
{
try
{
ManagedInstallerClass.InstallHelper(new string[] { "/LogFile=", Assembly.GetExecutingAssembly().Location });
}
catch
{
}
}
static void uninstallMyService()
{
try
{
ManagedInstallerClass.InstallHelper(new string[] { "/u", "/LogFile=", Assembly.GetExecutingAssembly().Location });
}
catch
{
}
}
|
|
|
|
 |
|
 |
Very nice, Aleksei. Nice addition.
Thanks,
|
|
|
|
 |
|
|
 |
|
 |
Firstly, very nice simple solution - thanks for sharing that tip. I decided to try it out and came up with the idea of installing the service if it's not already installed (i.e. just double click it in Explorer, and confirm whether or not to install it). Worked well for me, using this code:
if (args.Length == 0)
{
ServiceController sc = new ServiceController("AshFMS2");
try
{
string s = sc.Status.ToString(); }
catch
{
if (System.Windows.Forms.MessageBox.Show("Install this service? ", "Confirm", System.Windows.Forms.MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes)
{
SelfInstaller(SelfInstallOptions.Install);
return;
}
}
}
else
{
switch (args[0])
{
case "-i" :
case "/i" :
SelfInstaller(SelfInstallOptions.Install);
return;
case "-u" :
case "/u" :
SelfInstaller(SelfInstallOptions.Uninstall);
return;
}
}
BTW SelfInstaller is my method containing code based on yours.
------------------------------
My Latest CP article: SmartPager - a Flickr-style pager control with go-to-page popup layer.
modified on Saturday, December 22, 2007 12:01:11 AM
|
|
|
|
 |
|
 |
Very good, Ashley. Nice extension of the concept. It would be interesting to make
your code detect the version of the installed service and upgrade it on demand.
|
|
|
|
 |