MSIX でサービスをインストールしてみた

5 min read読了の目安(約5100字

Windows 10 2004 (2020/06 にリリースされたバージョン) から MSIX パッケージにサービスを含めることが可能になりました。

https://docs.microsoft.com/ja-jp/windows/msix/packaging-tool/convert-an-installer-with-services

ドキュメント上は、MSIX Packaging Tool を使う方法が書かれているので、ここでは手動構成をしてみようと思います。

Windows アプリケーション パッケージ プロジェクトを作成

まずは MSIX を作ってくれる Windows アプリケーション パッケージ プロジェクトを Visual Studio から使います。
作るときに対象の Windows 10 のバージョンを聞かれるので Min version と Target version を 2004 にしておきます。

サービスを作ろう

サービスの作り方は正直詳しくないのですが、Windows サービスを作るプロジェクトのひな型を見ながらコンソールアプリとして作っていきます。(今回は必要最低限のサービスを目指したので、コンソールアプリからはじめましたが普通に Windows サービスのプロジェクトテンプレートから作ってもいいです。)
コンソールアプリに System.ServiceProcess の参照を追加します。

そして、こんな感じで必要最低限だけ定義しておきます。

Program.cs
using System.IO;
using System.ServiceProcess;

namespace MyService
{
    class Program
    {
        static void Main(string[] args)
        {
            ServiceBase.Run(new MyService());
        }
    }

    class MyService : ServiceBase
    {
        protected override void OnStart(string[] args)
        {
            using (var sw = new StreamWriter("c:\\temp\\myservice.log", true))
            {
                sw.WriteLine("OnStart");
            }
            base.OnStart(args);
        }

        protected override void OnStop()
        {
            using (var sw = new StreamWriter("c:\\temp\\myservice.log", true))
            {
                sw.WriteLine("OnStop");
            }
            base.OnStop();
        }
    }
}

プロジェクトのプロパティで出力の種類をコンソール アプリケーションから Windows アプリケーションに変更しておきます。

Windows アプリケーション パッケージ プロジェクトの設定

最初に作った Windows アプリケーション パッケージ プロジェクトのアプリケーションに先ほど作った Windows サービスのプロジェクトを追加します。
ついでに適当な画面をもったプロジェクト(例えばWPFアプリ)もソリューションに追加して、これも Windows アプリケーション パッケージ プロジェクトに追加します。

こんな感じですね。

そして、Package.appxmanifest をコードで開いてサービスの定義のタグを追加します。
まずは xmlns:desktop6="http://schemas.microsoft.com/appx/manifest/desktop/windows10/6" を Package タグに追加して IgnorableNamespaces に desktop6 を追加します。

<Package
  xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
  xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
  xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
  xmlns:desktop6="http://schemas.microsoft.com/appx/manifest/desktop/windows10/6"
  IgnorableNamespaces="uap rescap desktop6">

そして、Application タグの下に Extensions タグを追加して、その中にサービスの定義を書きます。注意点としては Capabilities に以下の値を追加(両方、もしくは packagedServices のみ) を設定します。

  • packagedServices
  • localSystemServices

サービスが動くアカウントを指定する Service タグの StartAccount で localSystem を指定している場合は packagedServices と localSystemServices の両方の定義が必要です。(Capabilities タグが 2 つ必要)
localService か networkService の場合は packagedServices だけでいいです。以下の例では localService を指定しているので、packagedServices だけを指定しています。

Capabilities の詳細については、以下のアプリ機能の宣言のドキュメントの「パッケージ サービス」の項目も参照してください。

https://docs.microsoft.com/ja-jp/windows/uwp/packaging/app-capability-declarations

dekstop6:Extension タグの Executable 属性でサービスの exe を指定して EntryPoint に Windows.FullTrustApplication を設定します。そしてその下の desktop6:Service タグで表示名や起動のタイプ(auto, manual, diabled のどれか)と StartAccount (localSystem, localService, networkService のどれか)を設定します。

<Applications>
    <Application Id="App"
      Executable="$targetnametoken$.exe"
      EntryPoint="$targetentrypoint$">
        <uap:VisualElements
          DisplayName="Service.Package"
          Description="Service.Package"
          BackgroundColor="transparent"
          Square150x150Logo="Images\Square150x150Logo.png"
          Square44x44Logo="Images\Square44x44Logo.png">
            <uap:DefaultTile Wide310x150Logo="Images\Wide310x150Logo.png" />
            <uap:SplashScreen Image="Images\SplashScreen.png" />
        </uap:VisualElements>
        <Extensions>
            <desktop6:Extension Category="windows.service" Executable="MyService\MyService.exe" EntryPoint="Windows.FullTrustApplication">
                <desktop6:Service Name="MyService"
                                  StartupType="auto"
                                  StartAccount="localService">
                </desktop6:Service>
            </desktop6:Extension>
        </Extensions>
    </Application>
</Applications>

<Capabilities>
    <Capability Name="internetClient" />
    <rescap:Capability Name="runFullTrust" />
    <rescap:Capability Name="packagedServices" />
</Capabilities>

デバッグについて

サービスを含む Windows アプリケーション パッケージ プロジェクトは管理者権限で VIsual Studio を起動していないとデバッグ実行が出来ません。注意してください。
管理者権限無しでデバッグ実行をするとパッケージのインストールが出来ずにエラーになります。

サービスのデバッグは、F5 実行だけでは出来なかったので一度デバッグ実行するとサービスが裏で動くようになるので、明示的に自分でサービスの exe のプロセスにアタッチすることでブレークポイントが効くようになりました。

パッケージ化とインストール

パッケージ化は普通の MSIX と同じなので割愛しますが、インストールには管理者権限が必要になります。MSIX のパッケージをダブルクリックすると以下のように機能に「サービスをコンピューターにインストールします」が表示されます。インストールボタンに UAC のマークが出ている点も普通とは違うポイントですね。

まとめ

ということで、今回は Visual Studio でサービスを MSIX に含める方法について書いてみました。MSIX は結構色々機能強化されてるので見ていて楽しいですね。

ソースコードは以下の GitHub リポジトリに置いておきました。

https://github.com/runceel/Service.Package