Windows サービスあれこれ (3) - [スタートアップの種類] を変更してみましょう!

こんにちは、Platform SDK (Windows SDK) サポートの tomoshi です。
早いもので 2 月も半ばとなりましたね。
そろそろスギ花粉が猛威をふるう季節となりますが、皆様いかがお過ごしでしょうか。
実は先日から、スギ花粉のせいなのかどうか、すこぶる目と瞼の調子が良くありません。
眼科の診察を受けましたら「瞼に炎症が起きていますね」とのことで、ここ数日はずっと眼鏡で過ごしています。
普段はコンタクト レンズを使用していますので、一日中眼鏡で過ごすというのはなかなか新鮮な感じです!
これに乗じて、眼鏡を新しく作ろうかな・・・と画策しているところです。

 

さて今回も、simple サービス サンプルの 「知っ得★」なカスタマイズ方法をご紹介していきます。
既に皆様にもお馴染みのサンプルとなりましたでしょうか、Windows SDK から提供されている simple サービス サンプルをベースに、「あれこれ」ご紹介させていたければと思います。

なお、simple サービス サンプルの入手方法・ビルド方法は、「Windows サービスあれこれ (1)」でご紹介しておりますので、是非ご確認くださいね。

 

前回の「あれこれ」では、”Simple Service” サービスのプロパティの中から、[全般] タブの [サービス名] と [表示名] を変更する 「知っ得★」 をご紹介しました。
今回は、同じく [全般] タブの中から [スタートアップの種類] を変更する 「知っ得★」 をご案内いたします。

先ずはじめに、”Simple Service” サービスのプロパティを確認していきましょう。
simple サービス サンプルをインストールすると、コントロール パネルの [管理ツール] - [サービス] から ”Simple Servier” サービスを選択して、各種プロパティを参照することが可能です。

 

 

このプロパティ ダイアログ上から手動で変更可能な [スタートアップの種類] には、以下の 4 つが用意されています。

 

手動: システムの起動時に、サービス制御マネージャはこの Windows サービスを自動的に開始しません。そのため、後から好きなタイミングで明示的に開始することができます。

無効: この Windows サービスは開始できません。([スタート アップの種類] を他の種類に変更すれば、開始できるようになります)

自動: システムの起動時にサービス制御マネージャが自動的に開始します。

自動 (遅延開始) : システムの起動時にサービス制御マネージャが自動的に開始します。ただし、[自動] の Windows サービスよりも少し遅く開始されます。

 

既定では、simple サービス サンプルの [スタートアップの種類] は [手動] に設定されています。
[スタートアップの種類] は、このプロパティ ダイアログ上から手動で変更することが可能ですが、サービス インストールの段階で初期値として [スタートアップの種類] を設定させることも可能です。
つまり、Windows サービスの [スタートアップの種類] は、以下の二つの方法で変更することができるのです。

 

1. Windows サービスの作成時に変更する

2. インストール済みの Windows サービスを、後から変更する

 

1. の方法は、Windows サービスの作成時、つまりインストール時に初期値として [スタートアップの種類] を設定することになります。
これに対して 2. は、先程の Windows サービスのプロパティから、手動で [スタートアップの種類] を変更する場合と同じ方法ですね。
また、4 つ用意されている [スタートアップの種類] の内、[自動 (遅延開始)] については 2. の方法でしか設定することができない・・・という制限がありますので、ちょっと注意でございます。

2. の方法については後ほどご紹介するとして、先ずは 1. の方法について、私たちの simple サービス サンプルをベースにご説明さしあげましょう!

 

1. Windows サービスの作成時に [スタートアップの種類] を変更しよう!

Windows サービスは、Win32 API の CreateService() を呼び出すことによって、作成します。
simple サービス サンプルでは、Service.c の CmdInstallService() 内で、Win32 API の CreateService() 呼び出しが実装されています。
[スタートアップの種類] は、CreateService() の第四引数に指定した値で決定されますが、simple サービス サンプルでは [手動] にあたる SERVICE_DEMAND_START が指定されています。

 

      schService = CreateService(
                                schSCManager, // SCManager database
                                TEXT(SZSERVICENAME), // name of service
                                TEXT(SZSERVICEDISPLAYNAME), // name to display
                                SERVICE_QUERY_STATUS, // desired access
                                SERVICE_WIN32_OWN_PROCESS, // service type
                                SERVICE_DEMAND_START, // start type
                                SERVICE_ERROR_NORMAL, // error control type
                                szPath, // service's binary
                                NULL, // no load ordering group
                                NULL, // no tag identifier
                                TEXT(SZDEPENDENCIES), // dependencies
                                NULL, // LocalSystem account
                                NULL); // no password

 

この第四引数の値を、各 [スタートアップの種類] に応じて以下のように変更するだけで、初期値として、simple サービス サンプルの [スタートアップの種類] をインストール時に設定することが可能になります。

 

手動 : SERVICE_DEMAND_START

無効 : SERVICE_DISABLED

自動 : SERVICE_BOOT_START

 

それにしても、馴れない眼鏡のせいでしょうか・・・引数が多くて、目がチカチカしてきました・・・!

 

2. インストール済みの Windows サービスの [スタートアップの種類] を変更しよう!

先程、4 つ用意されている [スタートアップの種類] の内、[自動 (遅延開始)] については、インストール済みの Windows サービスに対して後から変更する必要があることをお伝えしました。
この [自動 (遅延開始)] は、Windows Vista ならびに Windows Server 2008 からサポートされた新しい [スタートアップの種類] です。
この種類を選択された Windows サービスは、[自動] を選択された Windows サービスよりも、Windows のサービス制御マネージャー (Service Control Manager) によって少し遅く自動開始されます。
実際には、[自動 (遅延開始)] の Windows サービスが開始される際に、Windows サービスのメイン スレッド (ServiceMain スレッド) のプライオリティが THREAD_PRIORITY_LOWEST に設定されます。
低くされたプライオリティは、[自動 (遅延開始)] の Windows サービスが、サービス制御マネージャーに対して SERVICE_RUNNING ステータスを通知して [開始] 状態になったタイミングで、THREAD_PRIORITY_NORMAL に再設定されます。
つまり [自動 (遅延開始)] とは、ServiceMain スレッドのプライオリティ操作によって、少しだけ開始タイミングを遅らせる仕組み、ということになりますね。

もちろん、先程の Windows サービスのプロパティから、手動で [自動 (遅延開始)] を選択していただくことも可能ですが、ここでは 「知っ得★」 ポイントとして、プログラムから変更する方法についてご紹介します。

 

インストール済みの Windows サービスの [スタートアップの種類] を変更する・・・という操作は、実は Windows サービスのコンフィギュレーションを変更する操作です。
Windows サービスのコンフィギュレーションは、Win32 API の ChangeServiceConfig() または、ChangeServiceConfig2() を呼び出すと、変更することが可能です。
特に [自動 (遅延開始)] については、ChangeServiceConfig2() でのみ、設定することが可能です。

そして・・・大変申し上げにくいのですが、私たちの simple サービス サンプルは、Windows サービスそのものと、そのインストール・アンインストール機能のみを実装したシンプルなサンプルであるため、この「Windows サービスのコンフィギュレーションを変更する」ためのプログラム コードが含まれておりません・・・!
大変大変お手数ではございますが、[スタートアップの種類] を変更するための新しいプログラムを、別にご用意いただく必要があります。

MSDN ライブラリに、Windows サービスのコンフィギュレーションを変更するためのサンプル コードが公開されておりますので、ここで是非ご紹介させてくださいませ。

 

SvcConfig.cpp
https://technet.microsoft.com/en-us/query/bb540473

 

このサンプル コードは、「Windows サービスあれこれ (1)」でもご紹介した Windows SDK の Command Prompt から、CL コマンドでビルド (コンパイル・リンク) が可能です。
サンプル コード全体をコピーしたら、拡張子 .c ファイルで保存 (例: SvcConfig.c) していただき、以下のように CL コマンドを実行して SvcConfig.c をビルドすると、実行可能ファイル (例: SvcConfig.exe) が生成されます。
もちろん、試しに実行して動作を楽しんで (?) いただくことも可能です。

 

このサンプル コードでは、Windows サービスのコンフィギュレーションの一つである [説明] を変更するために、DoUpdateSvcDesc() 内で ChangeServiceConfig2() を呼び出しています。
この ChangeServiceConfig2() 呼び出し部分を以下のように変更すると、コンフィギュレーションとして Windows サービスの [スタートアップの種類] を [自動 (遅延開始)] へ変更できるようになります。

 

      SERVICE_DELAYED_AUTO_START_INFO info = { TRUE };
      if( !ChangeServiceConfig2(
            schService, // handle to service
            SERVICE_CONFIG_DELAYED_AUTO_START_INFO
            &info) )
      {
            printf("ChangeServiceConfig2 failed\n");
      }

 

また、ChangeServiceConfig2() の第一引数に指定されている schService は、コンフィギュレーションの変更対象となる Windows サービスのハンドル (SC_HANDLE) 値です。
この schService の取得方法も、DoUpdateSvcDesc() 内に実装されていますので、あわせてご確認くださいね。

 

駆け足でのご案内となってしまいましたが、いかがでしたでしょうか。
[スタートアップの種類] から始まり、Windows サービスのコンフィギュレーション変更まで「あれこれ」欲張ってしまいました。
そして次回の「あれこれ」でも、もう少し Windows サービスのコンフィギュレーションについて、お話しするかもしれません。
(もちろん、simple サービス サンプルも一緒ですよ・・・!)