はじめに
C#でのWindowsサービス開発のはじめ方でとりあえずサービスの開発はできるのだが、インストールがめんどくさいのとインストールしたあとに自動で起動してくれないので、これをexe自身でできるように機能を組み込んでみる。
細かいことを考えるよりテンプレートとして使うほうがラクなので、一番下にProgram.csの内容をすべて載せておく。簡単な説明を見たあとでもいいし、面倒なら丸ごとコピペすればそれなりに動くと思う。
主要クラスの説明
インストール・アンインストールはManagedInstallerClassクラス
サービスをインストール・アンインストールにはManagedInstallerClass
クラスのInstallHelper(string[])
メソッドを使う。このメソッドは内部的にInstallUtil.exeを呼び出しているらしい。
インストール時は引数にexeのパスだけ、アンインストール時は”/u”とexeのパスを渡すだけだ。
var myAssembly = System.Reflection.Assembly.GetEntryAssembly();
string path = myAssembly.Location;
var param = (isInstallMode) ? new[] { path } : new[] { "/u", path };
ManagedInstallerClass.InstallHelper(param);
起動(開始)・停止はServiceControllerクラス
サービスの開始や停止にはServiceController
クラスのStart()
メソッド、Stop()
メソッドを使う。ServiceController
のコンストラクタにサービス名を渡すだけでよい。
起動(開始)
ServiceController sc = new ServiceController(SERVICE_NAME);
if (sc.Status != ServiceControllerStatus.Running)
{
sc.Start();
}
停止
ServiceController sc = new ServiceController(SERVICE_NAME);
if (sc.Status != ServiceControllerStatus.Stopped)
{
sc.Stop();
}
全体的なロジック
Main
メソッドではコマンドライン引数が渡されていれば、コンソールモードとして起動する。
コンソールモード(RunAsConsoleMode
メソッド内)では引数に応じて、インストール(/i)・アンインストール(/u)・開始(/start)・停止(/stop)を行う。
インストールでは終了後に自動的に開始し、アンインストールでは停止してからアンインストールを行うようにしてある。
ワンポイント
呼び出し方
exeファイル名をAwesomeService.exeだとすると
AwesomeService.exe /i
などとなる。
ソースコード (Program.cs)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
namespace WindowsService1
{
static class Program
{
public const string SERVICE_NAME = "AwesomeService";
public const string DISPLAY_NAME = "Awesome Service";
static void Main(string[] args)
{
// Run as console mode when an argument is provided.
if (1 <= args.Length)
{
RunAsConsoleMode(args[0]);
return;
}
// Run as Windows Service
ServiceBase.Run(new Service1()
{
CanShutdown = true,
CanPauseAndContinue = false,
});
}
/// <summary>
/// Run as console mode when an argument is provided.
/// </summary>
/// <param name="arg"></param>
/// <returns></returns>
static void RunAsConsoleMode(string arg)
{
string mode = arg.ToLower();
var myAssembly = System.Reflection.Assembly.GetEntryAssembly();
string path = myAssembly.Location;
if (mode == "/i" || mode == "/u")
{
bool isInstallMode = (mode == "/i");
var mes = (isInstallMode) ? "installed" : "uninstalled";
if (IsServiceExists())
{
Console.WriteLine("{0} has been already {1}.", DISPLAY_NAME, mes);
}
else
{
if (!isInstallMode) { StopService(); }
var param = (mode == "/i") ? new[] { path } : new[] { "/u", path };
ManagedInstallerClass.InstallHelper(param);
Console.WriteLine("{0} has been successfully {1}.", DISPLAY_NAME, mes);
if (isInstallMode) { StartService(); }
}
}
else if (mode == "/start")
{
StartService();
}
else if (mode == "/stop")
{
StopService();
}
else
{
Console.WriteLine("Provided arguments are unrecognized.");
}
}
/// <summary>
/// Whether the service already exists in the computer or not.
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
static bool IsServiceExists(string name = SERVICE_NAME)
{
ServiceController[] services = ServiceController.GetServices();
return services.Any(s => s.ServiceName == name);
}
/// <summary>
/// Start the service.
/// </summary>
/// <returns></returns>
static void StartService()
{
if (IsServiceExists())
{
Console.WriteLine("Starting {0}...", SERVICE_NAME);
ServiceController sc = new ServiceController(SERVICE_NAME);
if (sc.Status == ServiceControllerStatus.Running)
{
Console.WriteLine("{0} has been already started.", DISPLAY_NAME);
}
else
{
try
{
sc.Start();
Console.WriteLine("{0} has been started.", DISPLAY_NAME);
}
catch (Exception)
{
Console.WriteLine("{0} could not be started.", DISPLAY_NAME);
}
}
}
}
/// <summary>
/// Stop the service.
/// </summary>
/// <returns></returns>
static void StopService()
{
if (IsServiceExists())
{
Console.WriteLine("Stopping {0}...", SERVICE_NAME);
ServiceController sc = new ServiceController(SERVICE_NAME);
if (sc.Status == ServiceControllerStatus.Stopped)
{
Console.WriteLine("{0} has been already stopped.", DISPLAY_NAME);
}
else
{
try
{
sc.Stop();
Console.WriteLine("{0} has been stopped.", DISPLAY_NAME);
}
catch (Exception)
{
Console.WriteLine("{0} could not be stopped.", DISPLAY_NAME);
}
}
}
}
}
[RunInstaller(true)]
public class ProjectInstaller : Installer
{
public ProjectInstaller()
{
var spi = new ServiceProcessInstaller
{
Account = ServiceAccount.LocalSystem
};
var si = new ServiceInstaller
{
ServiceName = Program.SERVICE_NAME,
DisplayName = Program.DISPLAY_NAME,
Description = "Awesome Service.",
StartType = ServiceStartMode.Automatic,
};
this.Installers.Add(spi);
this.Installers.Add(si);
}
}
}