Visual Studioとセットアッププロジェクト(デプロイメントプロジェクト)を使用した場合、作成したWindowsサービスアプリケーションのインストーラを作成するのはとても簡単です。その方法はMSDNで詳しく説明されています。
この方法によると、「インストーラの追加」リンクをクリックすることにより、プロジェクトにInstallerクラスの派生クラス(ProjectInstallerクラス)を追加し、セットアッププロジェクトの「カスタム動作」にプロジェクトのプライマリ出力を追加するというものです。つまり、ProjectInstallerクラスにより、サービスのインストールとアンインストールを行います。ですので、このProjectInstallerクラスを処理することができれば、セットアッププロジェクトを使う必要がないということになります。
さて、Installerクラスによるカスタム動作がどのように行われるかについては「カスタム動作エディタの使い方」で解説しました。結論を言うと、InstallUtilが使われています。カスタム動作ではInstallUtilLib.dllが使われていますが、InstallUtil.exeを使ってもおなじです。
以上をまとめると、次のような方法でサービスのインストールが可能と言えます。
まずサービスのプロジェクトにProjectInstallerクラスを追加するところまではヘルプと同じです。あとはセットアッププロジェクトのカスタム動作の代わりにInstallUtil.exeを使って、インストール時に
InstallUtil.exe (サービスのEXEファイルのパス)
を、アンインストール時に
InstallUtil.exe /u (サービスのEXEファイルのパス)
を実行すればよいということになります。(つまり、インストール時とアンインストール時に実行ファイルを起動できないインストーラでは残念ながら無理です。)
ここで新たな問題が発生します。InstallUtil.exeはどこにあるのでしょうか?InstallUtil.exeを配布パッケージに含めることができれば確実ですが、残念ながらInstallUtil.exeは再配布が許可されていません。
そこでここでは、InstallUtil.exeは共通言語ランタイムがインストールされているディレクトリにあるものとして、これを実行するようにします。
共通言語ランタイムがインストールされているディレクトリのパスは、.NET Frameworkバージョン1.1であれば、
(Windowsディレクトリ)\Microsoft.NET\Framework\v1.1.4322
であると決め付けたり、レジストリキー
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework
の「sdkInstallRootv1.1」の値を調べて知ることができます。しかし.NET Frameworkが使えるのであれば、RuntimeEnvironment.GetRuntimeDirectoryメソッドを使うのが確実でしょう。
ここでは、InstallUtil.exeを起動させるために次のようなプログラムをC#で作成し、使用することにします。(ここでは、この実行ファイル名を「instsrv.exe」とします。)
[VB.NET] VB.NETのコードは現在準備中です。 Convert C# to VB.NET により、 下記のC#のコードをVB.NETに変換したものを参考にしてください。
public class InstallUtil { private static void Main(string[] args) { if (args.Length == 0) { ShowError("引数が不正です。"); return; } //installutil.exeのフルパスを取得 string installutilPath = System.IO.Path.Combine( System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory(), "installutil.exe"); if (!System.IO.File.Exists(installutilPath)) { ShowError("installutil.exeが見つかりませんでした。"); return; } //installutil.exeに渡すコマンドラインを作成 string installutilArg = ""; foreach (string arg in args) { installutilArg += " " + (arg.IndexOf(" ") > -1 ? "\"" + arg + "\"" : arg); } //installutil.exeを起動 System.Diagnostics.Process p; try { p = System.Diagnostics.Process.Start( installutilPath, installutilArg); p.WaitForExit(); } catch { ShowError("installutil.exeの起動に失敗しました。"); return; } if (p.ExitCode != 0) { ShowError("installutil.exeがエラーコード(" + p.ExitCode.ToString() + ")を返しました。"); return; } System.Environment.ExitCode = 0; } private static void ShowError(string msg) { System.Windows.Forms.MessageBox.Show( null, msg + "\nサービスのインストール/アンインストールに失敗しました。", "エラー", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error); System.Environment.ExitCode = 1; } }
この「instsrv.exe」をInstallUtil.exeと同じコマンドライン引数で呼び出すことにより、Installerクラスを処理できます。つまり、instsrv.exeを配布パッケージに含め、インストールで配置されるようにし、インストール時に
instsrv.exe (サービスのEXEファイルのパス)
を、アンインストール時に
instsrv.exe /u (サービスのEXEファイルのパス)
を実行すればサービスがインストール・アンインストールされるようになります。
具体例をひとつ示しましょう。ここでは「Inno Setup」を使います。
まず[Files]セクションを次のようにします(パスなどは適当に変更してください)。ここでは「WindowsService1.exe」がサービスのEXEファイル名です。instsrv.exeも配布対象にすることを忘れないでください。
[Files] Source: "C:\WindowsService1.exe"; DestDir: "{app}" Source: "C:\instsrv.exe"; DestDir: "{app}"
次にインストール時とアンインストール時にinstsrv.exeを起動するために、[Run]と[UninstallRun]セクションを次のようにします。
[Run] Filename: "{app}\instsrv.exe"; Parameters: "/LogFile= ""{app}\WindowsService1.exe""" [UninstallRun] Filename: "{app}\instsrv.exe"; Parameters: "/u /LogFile= ""{app}\WindowsService1.exe"""
これでサービスのインストールとアンインストールが行われるようになるでしょう。
このようにinstallutil.exeではなく、Win32 APIを使ってサービスをインストール・アンインストールする方法は、次の記事で紹介されています。
Inno SetupのPascal scriptingを使ってサービスをインストール・アンインストールする方法は、次の記事で紹介されています。
NSIS(Nullsoft Scriptable Install System)では、プラグインなどを使ってサービスのインストールとアンインストールができます。詳しくは、「How do I start/stop/create/remove/check a service」をご覧ください。
(この記事は、「.NETプログラミング研究」の第62号で紹介したものを基にしています。)