Without much ado, here's the code. All of it. Just replace ServiceMainThread with your code, and you're done. It even supports installing multiple instances of itself.
//-----------------------------------------------------------------------
// <copyright>
// Copyright (C) Sergey Solyanik.
//
// This file is subject to the terms and conditions of the Microsoft Public License (MS-PL).
// See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL for more details.
// </copyright>
//-----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Text;
using System.Threading;
namespace CSServiceHost
{
/// <summary>
/// Main service class.
/// </summary>
public class MyService : ServiceBase
{
/// <summary>
/// The thread that contains the execution path for the service.
/// </summary>
Thread runner;
/// <summary>
/// Event which gets signalled when the service stops.
/// </summary>
EventWaitHandle stop;
/// <summary>
/// Processes the start event for service.
/// </summary>
/// <param name="args"></param>
protected override void OnStart(string[] args)
{
stop = new EventWaitHandle(false, EventResetMode.ManualReset);
runner = new Thread(ServiceMainThread);
runner.Start();
}
/// <summary>
/// Processes the stop event for service.
/// </summary>
protected override void OnStop()
{
stop.Set();
runner.Join();
}
/// <summary>
/// The main entry point for the application.
/// </summary>
/// <param name="args"> Program arguments. See help.</param>
static void Main(string[] args)
{
if (args.Length > 0)
{
ProcessServiceCommand(args);
return;
}
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new MyService()
};
ServiceBase.Run(ServicesToRun);
}
/// <summary>
/// Executes service installation/uinstallation, or runs it as a process.
/// </summary>
/// <param name="args"> Program arguments.</param>
private static void ProcessServiceCommand(string[] args)
{
string exe = Assembly.GetExecutingAssembly().Location;
if ("/install".Equals(args[0], StringComparison.OrdinalIgnoreCase))
{
object[] assemblyAttributes =
Assembly.GetExecutingAssembly().GetCustomAttributes(false);
string instance =
(from a in assemblyAttributes
where a is AssemblyTitleAttribute
select ((AssemblyTitleAttribute)a).Title).SingleOrDefault();
string name =
(from a in assemblyAttributes
where a is AssemblyDescriptionAttribute
select ((AssemblyDescriptionAttribute)a).Description).SingleOrDefault();
string account = null;
string password = String.Empty;
for (int i = 1; i < args.Length; ++i)
{
if (args[i].StartsWith(
"/instance:", StringComparison.OrdinalIgnoreCase))
{
instance = args[i].Substring(10);
}
else if (args[i].StartsWith(
"/name:", StringComparison.OrdinalIgnoreCase))
{
name = args[i].Substring(6);
}
else if (args[i].StartsWith(
"/account:", StringComparison.OrdinalIgnoreCase))
{
account = args[i].Substring(9);
}
else if (args[i].StartsWith(
"/password:", StringComparison.OrdinalIgnoreCase))
{
password = args[i].Substring(10);
}
else
{
Console.Error.WriteLine("Could not parse: {0}", args[i]);
}
}
InstallService(exe, instance, name, account, password);
}
else if ("/uninstall".Equals(
args[0], StringComparison.OrdinalIgnoreCase))
{
object[] assemblyAttributes =
Assembly.GetExecutingAssembly().GetCustomAttributes(false);
string instance =
(from a in assemblyAttributes
where a is AssemblyTitleAttribute
select ((AssemblyTitleAttribute)a).Title).SingleOrDefault();
for (int i = 1; i < args.Length; ++i)
{
if (args[i].StartsWith(
"/instance:", StringComparison.OrdinalIgnoreCase))
{
instance = args[i].Substring(10);
}
else
{
Console.Error.WriteLine("Could not parse: {0}", args[i]);
}
}
UninstallService(instance);
}
else if ("/run".Equals(args[0], StringComparison.OrdinalIgnoreCase))
{
MyService service = new MyService();
service.OnStart(new string[0]);
Console.WriteLine("Service is running as a process.");
Console.WriteLine("Press <ENTER> to stop and exit.");
Console.ReadLine();
service.OnStop();
}
else
{
Console.WriteLine("To install service:");
Console.WriteLine(" {0} /install", exe);
Console.WriteLine(" [/instance:instance_name [/name:display_name]]");
Console.WriteLine(" [/account:account [/password:password]]");
Console.WriteLine("To uninstall service:");
Console.WriteLine(" {0} /uninstall [/instance:instance_name]", exe);
Console.WriteLine("To run as a regular process:");
Console.WriteLine(" {0} /run", exe);
}
}
/// <summary>
/// Installs service.
/// </summary>
/// <param name="exe"> Path to the executable. </param>
/// <param name="instance"> Name of the service instance. </param>
/// <param name="name"> Display name of the service. </param>
/// <param name="account"> Account name or NULL for LocalSystem. </param>
/// <param name="password"> Password or empty string if any of
/// the machine accounts. </param>
private static void InstallService(
string exe,
string instance,
string name,
string account,
string password)
{
IntPtr scm = Win32.OpenSCManager(
null, null, Win32.SC_MANAGER_CREATE_SERVICE);
if (scm.ToInt32() == 0)
{
Console.Error.WriteLine(
"Failed to open SCM (error {0}).", Win32.GetLastError());
return;
}
try
{
IntPtr service = Win32.CreateService(
scm,
instance,
name,
Win32.SERVICE_ALL_ACCESS,
Win32.SERVICE_WIN32_OWN_PROCESS,
Win32.SERVICE_AUTO_START,
Win32.SERVICE_ERROR_NORMAL,
exe,
null,
0,
null,
account,
password);
if (service.ToInt32() == 0)
{
Console.Error.WriteLine(
"Failed to create service (error {0}).",
Win32.GetLastError());
return;
}
try
{
if (Win32.StartService(service, 0, null) == 0)
{
Console.Error.WriteLine(
"Failed to start service (error {0}).",
Win32.GetLastError());
}
else
{
Console.WriteLine(
"Service installed successfully.");
}
}
finally
{
Win32.CloseServiceHandle(service);
}
}
finally
{
Win32.CloseServiceHandle(scm);
}
}
/// <summary>
/// Uninstalls service.
/// </summary>
/// <param name="instance"> Service instance. </param>
private static void UninstallService(string instance)
{
IntPtr scm = Win32.OpenSCManager(
null, null, Win32.SC_MANAGER_ALL_ACCESS);
if(scm.ToInt32() == 0)
{
Console.Error.WriteLine(
"Failed to open SCM (error {0}).",
Win32.GetLastError());
return;
}
try
{
IntPtr service = Win32.OpenService(
scm, instance, Win32.DELETE | Win32.SERVICE_STOP);
if (service.ToInt32() == 0)
{
Console.Error.WriteLine(
"Failed to open service (error {0}).",
Win32.GetLastError());
return;
}
try
{
Win32.SERVICE_STATUS stat;
if (0 == Win32.ControlService(
service, Win32.SERVICE_CONTROL_STOP, out stat))
{
Console.Error.WriteLine(
"Could not stop the service (error {0}).",
Win32.GetLastError());
}
while (Win32.QueryServiceStatus(service, out stat) != 0
&& stat.dwCurrentState != Win32.SERVICE_STOPPED)
{
Thread.Sleep(1000);
}
if (Win32.DeleteService(service) == 0)
{
Console.Error.WriteLine(
"Failed to delete service (error {0}).",
Win32.GetLastError());
}
else
{
Console.WriteLine(
"Service successfully uninstalled.");
}
}
finally
{
Win32.CloseServiceHandle(service);
}
}
finally
{
Win32.CloseServiceHandle(scm);
}
}
/// <summary>
/// The actual logic.
/// </summary>
private void ServiceMainThread()
{
for (; ; )
{
if (stop.WaitOne(10000))
{
break;
}
using (StreamWriter w =
new StreamWriter(@"c:\temp\testservice.txt", true))
w.WriteLine(
"Tick {0} {1}",
DateTime.Now,
Environment.UserName);
}
}
/// <summary>
/// Win32 thunks.
/// </summary>
private static class Win32
{
public const UInt32 SC_MANAGER_ALL_ACCESS = 0xF003F;
public const UInt32 SC_MANAGER_CREATE_SERVICE = 0x0002;
public const UInt32 SERVICE_WIN32_OWN_PROCESS = 0x00000010;
public const UInt32 SERVICE_AUTO_START = 0x00000002;
public const UInt32 SERVICE_ERROR_NORMAL = 0x00000001;
public const UInt32 STANDARD_RIGHTS_REQUIRED = 0xF0000;
public const UInt32 SERVICE_QUERY_CONFIG = 0x0001;
public const UInt32 SERVICE_CHANGE_CONFIG = 0x0002;
public const UInt32 SERVICE_QUERY_STATUS = 0x0004;
public const UInt32 SERVICE_ENUMERATE_DEPENDENTS = 0x0008;
public const UInt32 SERVICE_START = 0x0010;
public const UInt32 SERVICE_STOP = 0x0020;
public const UInt32 SERVICE_PAUSE_CONTINUE = 0x0040;
public const UInt32 SERVICE_INTERROGATE = 0x0080;
public const UInt32 SERVICE_USER_DEFINED_CONTROL = 0x0100;
public const UInt32 SERVICE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED |
SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG |
SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS |
SERVICE_START | SERVICE_STOP | SERVICE_PAUSE_CONTINUE |
SERVICE_INTERROGATE | SERVICE_USER_DEFINED_CONTROL;
public const UInt32 DELETE = 0x10000;
public const UInt32 SERVICE_CONTROL_STOP = 0x00000001;
public const UInt32 SERVICE_STOPPED = 0x00000001;
[StructLayout(LayoutKind.Sequential)]
public struct SERVICE_STATUS
{
public UInt32 dwServiceType;
public UInt32 dwCurrentState;
public UInt32 dwControlAccepted;
public UInt32 dwWin32ExitCode;
public UInt32 dwServiceSpecificExitCode;
public UInt32 dwCheckPoint;
public UInt32 dwWaitHint;
};
[DllImport("advapi32.dll")]
public static extern IntPtr OpenSCManager(
string lpMachineName,
string lpSCDB,
UInt32 scParameter);
[DllImport("advapi32.dll")]
public static extern IntPtr CreateService(
IntPtr SC_HANDLE,
string lpSvcName,
string lpDisplayName,
UInt32 dwDesiredAccess,
UInt32 dwServiceType,
UInt32 dwStartType,
UInt32 dwErrorControl,
string lpPathName,
string lpLoadOrderGroup,
int lpdwTagId,
string lpDependencies,
string lpServiceStartName,
string lpPassword);
[DllImport("advapi32.dll")]
public static extern void CloseServiceHandle(IntPtr SCHANDLE);
[DllImport("advapi32.dll")]
public static extern int StartService(
IntPtr SVHANDLE,
UInt32 dwNumServiceArgs,
string lpServiceArgVectors);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern IntPtr OpenService(
IntPtr SCHANDLE,
string lpSvcName,
UInt32 dwNumServiceArgs);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern int ControlService(
IntPtr SCHANDLE,
UInt32 dwControl,
[MarshalAs(UnmanagedType.Struct)]
out SERVICE_STATUS lpServiceStatus);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern int QueryServiceStatus(
IntPtr SCHANDLE,
[MarshalAs(UnmanagedType.Struct)]
out SERVICE_STATUS lpServiceStatus);
[DllImport("advapi32.dll")]
public static extern int DeleteService(IntPtr SVHANDLE);
[DllImport("kernel32.dll")]
public static extern int GetLastError();
}
}
}
No comments:
Post a Comment