レガシー Windows API(概要)について

ここで紹介しているAPIは、Windows95、Windows NT3.5時代のAPI群であり、最新のAPIは含まれていないことに注しして欲しい。なお、当時の解説書は現在よりもより詳細に解説しているため、参考になるはずである。

  • レイアウトを変換する際、崩れているページがあります。
  • 概要およびSampleコードはありますが、APIの解説はありません。

ご要望があれば旧版のWin32 API(HELP形式)をお譲りします。問い合わせフォームより申し込みください。

Windows API サービスの概要

Microsoft Win32アプリケーション プログラミング インターフェイス (API) の「サービス」とは、 サービス制御マネージャが管理するレジストリ データベースにインストールされている実行可能オブジェクトです。サービス データベースには、 インストールされている各サービスを要求に応じて起動するか、 それともシステムの起動時に自動的に起動するかを示す情報があります。また、 データベースにはサービスのログオン情報やセキュリティ情報があり、 ユーザーがログオンしていなくてもサービスは動作できます。さらに、 システム管理者は、 各サービスのセキュリティ条件をカスタマイズして、 サービスへのアクセスを制御できます。サービスのインスタンスを同時に複数実行することはできません。

サービスには2つのグループがあります。サービス制御マネージャのインターフェイス規則にしたがうWin32サービスと、 Windows NTのデバイス ドライバ プロトコルにしたがうドライバ サービスです。

次に示すトピックでは、 Win32サービスについて説明しています。

  • サービス制御マネージャ

  • インストールされているサービスのデータベース

  • Win32サービス プロセス

  • サービス構成プログラム

  • サービス制御プログラム

  • サービスのセキュリティ

  • 対話サービス

  • サービスの使用

  • サービス プログラムのmain()の作成

  • SERVICE_MAIN_FUNCTIONの作成

  • HANDLER_FUNCTIONの作成

  • SCManagerデータベースのオープン

  • サービスのインストール

  • サービスの削除

  • サービスの構成の変更

  • サービスの起動

  • サービスの構成の問い合わせ

  • サービスの制御

サービス制御マネージャ

「サービス制御マネージャ」は、 サービスを統一的にセキュリティ付きで制御する方法を用意しています。Windows NTが動作している各マシンには、 システムが起動時に起動したサービス制御マネージャ プロセスが1つあります。サービス制御マネージャは、 リモート プロシージャ コール (RPC) サーバーです。サービス コントロール マネージャにアクセスするWin32 API関数はRPCによって実現されているため、 サービス設定プロセスやサービス制御プロセスは、 リモート マシンのサービスを操作できます。

Win32 APIには、 サービス制御マネージャが行う次のような作業のプログラミング インターフェイスを提供する関数のセットがあります。

インストールされているサービス オブジェクトのデータベースのレジストリによる管理

システムの起動時やサービスが要求されたときのサービスの起動

動作中のサービスの状態情報の管理

動作中のサービスへの制御要求の転送

上記の作業を行う関数を使うプログラムには、 次の3つの種類があります。

種類 説明

Win32サービス プロセス 1つ以上のサービスの実行可能コードを提供するプロセスです。Win32サービスは、 サービス制御マネージャに接続する関数や、 状態情報を送る関数を使います。ドライバ サービスはそのような関数を使いません。

サービス構成プログラム サービス制御マネージャ データベースを問い合わせたり修正するプログラムです。サービス構成プログラムは、 データベースをオープンする関数、 データベースにサービスをインストールしたり削除する関数、 インストールされているサービスの設定パラメータやセキュリティ パラメータを問い合わせたり修正する関数を使います。Win32サービスやドライバ サービスを操作するには、 関数を使います。

サービス制御プログラム サービスを起動したり、 動作中のサービスを制御するプログラムです。サービス制御プログラムは、 サービス制御マネージャに要求を送る関数を使います。サービス制御マネージャは、 要求されたサービスを起動したり、 制御要求を指定されたサービスに送ります。サービス制御プログラムはWin32サービスとドライバ サービスの両方を起動できますが、 制御要求にこのインターフェイスを使うのはWin32サービスだけです。

インストールされているサービスのデータベース

サービス制御マネージャは、 構成レジストリでServicesActiveデータベースを管理しています。このデータベースは、 システムの起動に使われた、 現在アクティブなデータベースです。このデータベースへのレジストリ パスは、 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSe_\Servicesです。このディレクトリには、 インストールされているWin32サービスやドライバ サービスごとに、 <ServiceName>キーがあります。<ServiceName>は、 サービスをインストールするときにCreateService関数で指定したサービスの名前です。

データベースには、 インストールされている各サービスに関する次のような情報があります。

サービスの種類。Win32サービスの場合、 サービスの種類は、 サービスが専用のプロセスで実行されるか、 ほかのWin32サービスとプロセスを共有するかを示します。ドライバ サービスの場合、 サービスの種類は、 サービスがカーネル ドライバかファイル システム ドライバかを示します。

サービスの起動の種類。起動の種類は、 サービスをシステム起動時に自動的に起動するか、 それともサービス制御プロセスが要求したときにサービス制御マネージャが起動するかを示します。また、 起動の種類は、 サービスを使用不能にするかどうかも示します。使用不能のサービスは起動できません。

サービスの実行可能ファイルのフル パス。Win32サービスのファイル名拡張子は.EXEで、 デバイス サービスの拡張子は.SYSです。

サービス制御マネージャがサービスの起動順序を判断するために使う使うオプションの情報。この情報には、 特定のサービスよりも前に起動しなければならないサービスのリスト、 サービスが属するロード順序グループの名前、 ロード順序グループ内でのサービスの起動順序を示すタグ識別子があります。

Win32サービスの場合、 サービス プロセスを実行するためのオプションのログオン アカウント名と、 そのアカウント名のパスワード (オプション) があります。アカウントを指定しなければ、 サービスはLocalSystemアカウントで動作します。

ドライバ サービスの場合、 オプションのNTドライバ オブジェクト名 (\FileSystem\Rdr\Driver\Xnsなど) があります。I/Oシステムは、 この名前を使ってデバイス ドライバをロードします。名前を指定しなければ、 I/Oシステムは、 サービス名に基づいてデフォルト名を作成します。

Win32サービス プロセス

Win32サービス プロセスには、 1つ以上のサービスの実行可能コードが含まれます。サービス制御マネージャのインターフェイス規定に従うには、 次に示す要素がなければなりません。

すぐにStartServiceCtrlDispatcher関数を呼び出して、 プロセスのメイン スレッドをサービス制御マネージャに接続するmain関数。

プロセスで動作する各サービスごとのServiceMainエントリ ポイント関数。

プロセスで動作する各サービスごとのHandler制御ハンドラ関数。

ここでは、 「サービス」という用語を「Win32サービス」の意味に使います。ここで説明する情報は、 ドライバ サービスには適用されません。

メイン関数

サービス制御マネージャは、 サービス プロセスを起動するとき、 プロセスがStartServiceCtrlDispatcher関数を呼び出すのを待ちます。サービスがほかのサービスとプロセスを共有しないとき (SERVICE_WIN32_OWN_PROCESSサービスのとき)、 サービス プロセスのメイン スレッドは、 このスレッドをすぐに呼び出さなければなりません。初期化作業は、 すべて、 サービスが起動してからServiceMain関数で実行してください。複数のサービスが1つのプロセスを共有し (SERVICE_WIN32_SHARE_PROCESSサービスの場合) ServiceMain関数を呼び出す前にプロセス間で共通な初期化が必要なときは、 メイン スレッドは、 StartServiceCtrlDispatcherを呼び出す前に、 30秒以内の作業を実行できます。作業に30秒以上かかるときは、 プロセス間で共通な初期化を行う別のスレッドを作成し、 メイン スレッドはStartServiceCtrlDispatcherを呼び出してください。どちらの場合も、 サービス固有の初期化は、 個々のServiceMain関数で行ってください。

StartServiceCtrlDispatcherは、 正常に終了すると、 呼び出し側のスレッドをサービス制御マネージャに接続し、 プロセスで操作中のサービスがすべて終了するまで戻りません。サービス制御マネージャは、 この接続を使って、 制御要求やサービス起動要求をサービス プロセスのメイン スレッドに送ります。メイン スレッドは、 適切なHandler関数を呼び出して制御要求を処理したり、 適切なServiceMain関数を実行する新しいスレッドを新しいサービスを起動するときに作成することによって、 ディスパッチャとして動作します。

StartServiceCtrlDispatcher関数には、 呼び出し側プロセスで動作する各サービスのエントリを含むディスパッチ テーブルを指定します。各エントリは、 サービスのServiceMain関数を示します。SERVICE_WIN32_SHARE_PROCESSサービスの場合、 各エントリにはサービスの名前がなければなりません。この名前は、 サービスをインストールするときにCreateService関数で指定したサービス名です。SERVICE_WIN32_OWN_PROCESSサービスの場合、 テーブル エントリのサービス名は無視されます。

サービス メイン関数

ServiceMain関数は、 サービスのエントリ ポイントです。サービス制御プログラムがStartService関数を呼び出してサービスを起動すると、 サービス制御マネージャは、 サービス プロセスを (まだ起動していなければ) 起動します。その後、 サービス制御マネージャは、 サービス プロセスのメイン スレッドの制御ディスパッチャに起動要求を送ります。ディスパッチャは、 指定されたサービスのServiceMain関数を実行する新しいスレッドを作成します。

ServiceMain関数では、 次に示す作業をこの順番で実行してください。

1. すぐにRegisterServiceCtrlHandler関数を呼び出して、 サービスの制御要求を処理するHandler関数を登録します。RegisterServiceCtrlHandlerの戻り値はSERVICE_STATUS_HANDLE型の値で、 このハンドルはSetServiceStatus関数を呼び出すときにサービスを識別するのに使います。サービスは、 SetServiceStatusを呼び出して、 サービス制御マネージャにサービスの状態を知らせます。

2. 初期化を行います。初期化コードの実行に時間がかからない (1秒より短い) 場合はサービスは、 ServiceMainで直接実行できます。初期化にかかる時間が1秒より長いは、 現在のサービス状態をSERVICE_START_PENDINGにしてSetServiceStatusを呼び出します。初期化の進行中、 サービスはさらにSetServiceStatusを呼び出して進行状態を報告します。SetServiceStatus呼び出しを何度も送ることは、 サービスのデバッグとプロダクトのサポートのために便利です。

SetServiceStatus呼び出しに指定するSERVICE_STATUS構造体の次に示すメンバは、 初期化の進行に応じて変更してください。

メンバ

dwCurrentState この値は初期化中はSERVICE_START_PENDINGに設定してください。初期化が完了したら、 SERVICE_RUNNINGに変更してください。

dwControlsAccepted 初期化中は、 制御コードをすべて使用不能にしてください。初期化が完了したら、 適切な制御コードを使用可能にしてください。

dwCheckPoint SetServiceStatusを呼び出すたびにこの値を増やしてください。この値によって、 サービス制御マネージャは時間のかかる初期化の進行状況を知ります。

dwWaitHint この値は、 次の初期化段階を完了するのにかかる時間の目安になります。この値は、 最悪の場合を予想して、 次の未処理のSetServiceStatus呼び出しがされるまでに経過する時間をミリ秒単位で示さなけばなりません。この時間が経過してもdwCheckPointのチェックポイント値が増加していなければ、 サービスを起動したサービス制御マネージャかサービス制御プログラムは、 エラーが発生したとみなす場合があります。

3. 初期化が完了したら、 現在の状態をSERVICE_RUNNINGに指定してSetServiceStatusを呼び出します。ServiceMain関数は、 その後サービス タスクの実行を続けるか、 未処理のタスクがない場合は戻ります。サービス認可の状態が変更したら、 新しい状態を報告するためにSetServiceStatusを呼び出します。エラーのためにサービスが停止した場合は、 サービスはエラーを識別するためにSERVICE_STATUS構造体のdwServiceSpecificExitCodeメンバとdwWin32ExitCodeメンバを使わなければなりません。

制御ハンドラ関数

Win32サービスには、 制御ハンドラ関数のHandler関数があります。サービスが制御要求を受け取ると、 メイン スレッドの制御ディスパッチャはこの関数を呼び出します。サービスは、 ServiceMain関数でRegisterServiceCtrlHandlerエントリ ポイント関数を呼び出すときに、 この関数のアドレスを指定します。制御要求は、 ControlService関数を呼び出すサービス制御プログラムによって生成されます。

サービスのHandler関数が呼び出されたら、 サービスは、 SetServiceStatus関数を呼び出して自分の状態をサービス制御マネージャに報告しなければなりません。この報告は、 サービスの状態が変化していなくても行ってください。

ControlService関数には、 次の表に示す標準制御コードを指定します。どのサービスも、 SERVICE_CONTROL_INTERROGATEコードを受け付けて処理しなければなりません。サービスは、 SetServiceStatusを呼び出すとき、 これ以外の標準制御コードを使用可能または使用不能にできます。また、 サービスは、 ユーザー定義制御コードを処理できます。ユーザー定義制御コードは、 128から255までの間でなければなりません。

制御コード 意味

SERVICE_CONTROL_STOP

サービスの停止を要求します。

SERVICE_CONTROL_PAUSE

サービスの一時停止を要求します。

SERVICE_CONTROL_CONTINUE

一時停止しているサービスの再開を要求します。

SERVICE_CONTROL_INTERROGATE

現在の状態情報をすぐに更新してサービス制御マネージャに知らせるようにサービスに要求します。

SERVICE_CONTROL_SHUTDOWN

システムがシャット ダウンしようとしているため、 サービスに後始末を要求します。シャット ダウン時に利用可能な時間は非常に限られているため、 この制御要求は、 シャット ダウンが絶対に必要なサービスだけに使ってください。たとえば、 イベントログ サービスでは管理対象のファイルの変更ビットをクリアしなければなりません。また、 システムがシャット ダウン状態のときはネットワーク接続はできないため、 サーバー サービスをシャット ダウンしなければなりません。

サービスのシャット ダウンに時間がかかり、 STOP_PENDING状態メッセージを何度も送る場合、 メッセージには必ず待機ヒントを指定して、 サービスのシャット ダウンが完了したことをシステムに通知するまでサービス制御マネージャが待つべき時間を知らせてください。サービス制御マネージャがサービスのシャット ダウンに使える時間は限られています (20)。この時間が経過すると、 サービスのシャット ダウンが完了したかどうかに関わらず、 システムのシャット ダウン処理は進行します。

サービス構成プログラム

サービス構成プログラムは、 プログラマやシステム管理者がサービス制御マネージャのデータベースを問い合わせたり制御するために使われます。このデータベースは構成レジストリで管理されているため、 レジストリ関数やレジストリ エディタを使ってアクセスすることもできます。しかし、 サービス制御マネージャの構成関数を使って、 サービスが正しくインストールされ、 設定されているようにしたほうが安全です。

サービス制御マネージャのデータベースやサービスはオブジェクトとして実現されており、 アクセスするときはサービス制御マネージャの構成関数だけを使ってください。インストールされているサービスのデータベースはSCManagerオブジェクトで表現され、 インストールされているサービスはサービス オブジェクトで表現されます。SCManagerオブジェクトは、 サービス オブジェクトを含むコンテナ オブジェクトです。

SCManagerオブジェクトやサービス オブジェクトのハンドルの型は、 SC_HANDLEです。OpenSCManager関数は、 指定されたマシンのSCManagerオブジェクトのハンドルを返します。CreateService関数とOpenService関数は、 指定されたSCManagerオブジェクトにインストールされているサービス オブジェクトのハンドルを返します。CloseServiceHandle関数は、 SCManagerオブジェクトやサービス オブジェクトのハンドルをクローズします。

SC_HANDLEを返す関数には、 SCManagerオブジェクトやサービス オブジェクトへのさまざまな種類のアクセスを要求できます。要求されたアクセスは、 呼び出し側プロセスのアクセス トークンと、 SCManagerオブジェクトやサービス オブジェクトに関連付けられているセキュリティ記述子に応じて、 許可または拒否されます。

構成プログラムは、 CreateService関数を使って、 サービス制御マネージャ データベースに新しいサービスをインストールします。この関数には、

サービスの名前と、 データベースに格納するサービス構成情報を指定します。各サービスのデータベースに格納される情報について詳しくは、 インストールされているサービスのデータベースを参照してください。DeleteService関数は、 データベースにインストールされているサービスを削除します。

サービス制御マネージャ データベースの現在の設定を調べるには、 次に示す関数を使います。

関数 説明

QueryServiceConfig 指定されたサービス オブジェクトの現在の構成情報を示すQUERY_SERVICE_CONFIG構造体を、 供給されたバッファに設定します。

QueryServiceObjectSecurity 指定されたSCManagerオブジェクトかサービス オブジェクトのSECURITY_DESCRIPTOR構造体をバッファに設定します。

EnumDependentServices 指定されたサービス オブジェクトに依存するすべてのサービスの名前と現在の状態を示すENUM_SERVICE_STATUS構造体の配列をバッファに設定します。

EnumServicesStatus 指定されたSCManagerオブジェクトに依存するすべてのサービスの名前と現在の状態を示すENUM_SERVICE_STATUS構造体の配列をバッファに設定します。

構成プログラムがサービス オブジェクトの構成情報を修正するには、 ChangeServiceConfig関数を使います。また、 SCManagerオブジェクトやサービス オブジェクトのセキュリティ記述子を修正するには、 SetServiceObjectSecurity関数を使います。

プログラムは、 上記の関数を使ってサービス オブジェクトを再設定する前に、 LockServiceDatabase関数を呼び出してください。この関数は、 指定されたサービス制御マネージャ データベースをロックしようとします。データベースが正常にロックされると、 データベースの再構成中は、 サービス制御マネージャはサービスを起動しなくなります。また、 サービス オブジェクトを再構成するレジストリ関数を呼び出す前にもロックを獲得してください。しかし、 データベースをロックできなくても、 構成プログラムがサービス オブジェクトを正常に構成できないわけではありません。データベースのロックの効果は、 サービス制御マネージャがロックされているデータベースのサービスを起動しないようにすることだけです。再構成が完了したときにデータベースのロックを解放するには、 UnlockServiceDatabase関数を使います。データベースがロックされているか調べるには、 QueryServiceLockStatus関数を使います。

サービス制御プログラム

サービス制御プログラムは、 次に示す処理を実行します。

起動の種類がSERVICE_DEMAND_STARTWin32サービスやドライバ サービスを起動します。

動作中のサービスに制御要求を送ります。

動作中のサービスの現在の状態を問い合わせます。

プログラムがサービスを起動したり制御するには、 サービス オブジェクトのハンドルをオープンしなければなりません。ハンドルをオープンするには、 次に示す手順を実行します。

1.OpenSCManager関数を使って、 特定のマシンのサービス制御マネージャ データベースのハンドルをオープンします。

2.OpenService関数かCreateService関数を使って、 必要なサービス オブジェクトのハンドルをオープンします。

サービスの起動

サービスを起動するには、 StartService関数を使います。Win32サービスの場合、 呼び出し側のプロセスは、 この関数のパラメータを使って、 サービスのServiceMain関数に渡される引数の配列を指定できます。サービス制御マネージャのデータベースがロックされていると、 この関数は失敗します。その場合、 サービス制御マネージャは、 数秒間待ってからもう一度StartServiceを呼び出してください。データベースの現在のロック状態を調べるには、 QueryServiceLockStatus関数を使います。

ドライバ サービスを起動するとき、 StartServiceは、 デバイス ドライバが初期化を完了してから戻ります。

Win32サービスを起動するとき、 StartServiceは、 新しいサービスのServiceMain関数スレッドが作成されてから戻ります。サービスは、 初期化中にSERVICE_STATUS構造体を指定してSetServiceStatus関数を呼び出して、 初期化の進行状態をサービス制御マネージャに報告します。サービス制御プログラムは、 QueryServiceStatus関数を呼び出してこの状態情報を取得できます。初期化中は、 SERVICE_STATUS構造体のdwCurrentStateメンバには、 SERVICE_START_PENDINGを指定してください。初期化中、 サービスは、 一定間隔でdwCheckPointメンバの値を増やしてください。dwWaitHintメンバには、 サービス制御プログラムがもう一度QueryServiceStatusを呼び出すまで待つべき時間をミリ秒単位で指定します。サービスは、 初期化が完了したら、 dwCurrentStateメンバをSERVICE_RUNNINGに変更してください。

サービス制御要求

動作中のサービスに制御要求を送るには、 ControlService関数を使います。この関数には、 指定したサービスのHandler関数に渡される制御コードを指定します。この制御コードには、 ユーザー定義のコードか、 呼び出し側プログラムから次に示す動作を実行するための標準コードのいずれかを指定できます。

サービスの停止または一時停止。

一時停止しているサービスの再開。

サービスの最新状態情報の取得。

各サービスには、 それぞれ、 受け付けて処理できる制御コードがあります。サービスが受け付ける標準制御コードを調べるには、 QueryServiceStatus関数を使うか、 SERVICE_CONTROL_INTERROGATE制御コードを指定してControlService関数を呼び出します。これらの関数が返すSERVICE_STATUS構造体のdwControlsAcceptedメンバは、 サービスの停止が可能かどうかと、 サービスの一時停止や再開が可能かどうかを示します。SERVICE_CONTROL_INTERROGATE制御コードは、 すべてのサービスが受け付けます。このコードは、 現在の状態をすぐに報告するようにサービスに要求します。

しかし、 QueryServiceStatus関数は、 指定されたサービスについてサービス制御マネージャが管理している中で最新の状態を返しますが、 サービス自身が持っている最新の状態は取得しません。最新の状態情報を取得するには、 SERVICE_CONTROL_INTERROGATE制御コードを指定してControlServiceを呼び出してください。

サービスのセキュリティ

プロセスがOpenSCManager関数を使ってサービス制御マネージャ データベースのハンドルをオープンすると、 システムは、 要求されたアクセスを許可する前に、 セキュリティ チェックを行います。すべてのサービス制御マネージャ データベースへのSC_MANAGER_CONNECTアクセス、 SC_MANAGER_ENUMERATE_SERVICEアクセス、 SC_MANAGER_QUERY_LOCK_STATUSアクセスは、 すべてのプロセスに許可されています。プロセスは、 このアクセスによってサービス制御マネージャ データベースのハンドルをオープンし、 OpenService関数、 EnumServicesStatus関数、 QueryServiceLockStatus関数で使うことができます。CreateService関数やLockServiceDatabase関数で使われるデータベース ハンドルをオープンできるのは、 管理者特権を持つプロセスだけです。

サービス オブジェクトのセキュリティは、 プロセスがOpenService関数を呼び出したときに行われるセキュリティ チェックで実現されています。各ユーザーに許可されるアクセスは、 サービス オブジェクトに関連付けられているSECURITY_DESCRIPTOR構造体によって異なります。CreateService関数でサービスをインストールすると、 サービス制御マネージャは、 サービス オブジェクトのセキュリティ記述子を作成します。サービス オブジェクトのセキュリティ記述子を問い合わせたり設定するには、 QueryServiceObjectSecurity関数とSetServiceObjectSecurity関数を使います。サービス オブジェクトのデフォルトのセキュリティ記述子は、 次に示すアクセスを許可します。

すべてのユーザーのSERVICE_QUERY_CONFIGSERVICE_QUERY_STATUS SERVICE_ENUMERATE_DEPENDENTSSERVICE_INTERROGATESERVICE_USER_DEFINED_CONTROLの各アクセス

ローカル ユーザーに対しては、 上記のアクセスのほかに、 SERVICE_START SERVICE_PAUSE_CONTINUESERVICE_STOPの各アクセス

LocalSystemユーザーとパワー ユーザーには、 さらにSERVICE_STOPアクセス

管理者にはSERVICE_ALL_ACCESSアクセス

Win32サービス ユーザー アカウント

Win32サービスは、 ユーザー アカウントで動作します。アカウントのユーザー名とパスワードは、 サービスをインストールするときにCreateService関数で指定します。ユーザー名とパスワードを変更するには、 ChangeServiceConfig関数を使います。

サービス オブジェクトの (パスワードを取得せずに) ユーザー名だけを取得するには、 QueryServiceConfig関数を使います。

サービス制御マネージャは、 サービス プロセスを起動するとき、 そのサービスに関連付けられているアカウントでログオンします。正常にログオンできれば、 システムはアクセス トークンを生成して新しいサービス プロセスに付加します。このトークンは、 これ以降、 セキュリティ付きオブジェクト (セキュリティ記述子が関連付けられているオブジェクト) との対話でサーバー プロセスを識別するのに使われます。たとえば、 サービスがパイプのハンドルをオープンしようとすると、 システムは、 アクセスを許可する前に、 サービスのアクセス トークンとパイプのセキュリティ記述子を比較します。

専用のプロセスで動作するSERVICE_WIN32_OWN_PROCESSサービスは、 組み込み (ローカル) ドメイン、 プライマリ ドメイン、 信頼関係にあるドメインのいずれかのアカウントを持つことができます。複数のサービスで1つのプロセスを共有するSERVICE_WIN32_SHARE_PROCESSサービスは、 LocalSystemアカウントで動作するようにインストールしなければなりませ

サービス制御マネージャは、 サービス ユーザー アカウントのパスワードを管理しません。パスワードの期限が切れると、 ログオンは失敗し、 サービスは起動できなくなります。サービスにアカウントを割り当てるシステム管理者は、 永久に有効なパスワードを持つアカウントを作成できます。また、 管理者は、 サービス構成プログラムを使ってパスワードを定期的に変更することによって、 パスワードに期限があるアカウントを管理することもできます。

サービスがほかのサービスと情報を共有する前にそのサービスを認識しなければならない場合、 第2のサービスは、 第1のサービスと同じアカウントを使うか、 第1のサービスが認識するエイリアスに属するアカウントで動作できます。ネットワーク上で分散動作しなければならないサービスは、 ドメイン全体で有効なアカウントで動作しなければなりません。

サービスの使用

ここの例では、 次のものを示します。

  • Win32サービス プロセスのmain関数

  • Win32サービス プロセスのServiceMainエントリ ポイント関数

  • Win32サービス プロセスのHandler制御ハンドラ関数

  • サービス制御マネージャ データベースのオープン

  • サービス制御マネージャ データベースへのサービスのインストールと削除

  •  インストールされているサービスの設定の変更や問い合わせ

  • サービスの起動

  • 動作中のサービスへの制御要求の送信

Win32サービス プロセスのmain関数の作成

Win32サービス プロセスのmain関数はStartServiceCtrlDispatcher関数を呼び出してサービス制御マネージャに接続し、 制御ディスパッチャ スレッドを起動します。ディスパッチャ スレッドはディスパッチ テーブルに指定されたサービス要求が来るのをループして待ちます。このスレッドはエラーが起きるか、 プロセスのすべてのサービスが終了するまで戻りません。プロセスのすべてのサービスが終了すると、 サービス制御マネージャはディスパッャ スレッドにシャット ダウンを告げる制御要求を送ります。そこでスレッドは、 StartServiceCtrlDispatcher呼び出しから戻り、 プロセスは終了します。

この節で提供されている例は、 1つのサービスだけをサポートするサービス プロセスを示しています。1つのサービスしかサポートしていないプロセスは、 エントリポイント関数であるServiceMainで必要な初期化をすべて実行できるので、 すぐにStartServiceCtrlDispatcherを呼び出すことができます。

サービス プロセスが複数のサービスをサポートする場合、 main関数の実装は少し異なります。追加のサービスがディスパッチャ スレッドで監視できるように、 それらの名前をディスパッチ テーブルに追加します。マルチ サービス プロセスによってはStartServiceCtrlDispatcher呼び出しでサービスを起動する前に、 プロセス全体用の初期化の処理の実行が必要な場合があります。この場合に、 初期化が簡単 (30秒かからない) ならば、 mainスレッドはStartServiceCtrlDispatcher呼び出しの前にそれを行うことができます。この初期化の処理に30秒以上かかる場合は、 次に示す方法のどれかを使います。

プロセス全体用の初期化を実行するスレッドを作成するためにCreateThreadを呼び出す。

起動する最初のサービスがプロセス全体用の処理を実行できるように、 各サービスがそれぞれの初期化の間にチェックするグローバル フラグを使う。

次の例では、 デバッガに情報メッセージとエラーを表示するSvcDebugOutというローカル関数と、 RegisterServiceCtrlHandlerが失敗したときに、 詳しいエラー コードを取得するWin32関数のGetLastErrorを呼び出します。SvcDebugOutは書式化された1つの出力文字を含む文字列と、 書式化文字として使われる数値の2つをパラメータに持ちます。

/* Globals */
SERVICE_STATUSMyServiceStatus;
SERVICE_STATUS_HANDLEMyServiceStatusHandle;

/* Function Prototypes */
VOIDMyServiceStart (DWORDargc, LPTSTR*argv);
VOIDMyServiceCtrlHandler (INDWORD opcode);
DWORD MyServiceInitialization(DWORD argc, LPTSTR*argv,
DWORD*specificError);

VOID _CRTAPI1 main(void)
{
SERVICE_TABLE_ENTRYDispatchTable[] = {
{ TEXT("MyService"),MyServiceStart},
{ NULL,NULL}

};

if (!StartServiceCtrlDispatcher( DispatchTable)) {
SvcDebugOut(" [MY_SERVICE] StartServiceCtrlDispatcher error =
%d\n", GetLastError());
}
}


VOID SvcDebugOut(LPSTR String, DWORD Status)
{
CHARBuffer[1024];
if (strlen(String) < 1000){
sprintf(Buffer, String, Status);
OutputDebugStringA(Buffer);
}
}

ServiceMain関数の作成

次の例のMyServiceStart関数は、 サービスのエントリ ポイントです。制御ディスパッチャがサービスを起動するように告げられると、 ここで実行を開始するスレッドを作成します。MyServiceStartは通常のmain関数と同じようにコマンド ライン引数を使います。最初のパラメータはサービスに渡される引数の個数を格納します。常に、 少なくとも1つ以上の引数があります。2番目のパラメータは文字列ポインタの配列へのポインタです。配列の最初の項目は常にサービス名を指します。

まず、 MyServiceStartは受け取る制御コードを含むSERVICE_STATUS構造体を埋めます。このサービスはPAUSE_CONTINUEを受け取りますが、 中断を要求しても特に何もしないことに注意してください。PAUSE_CONTINUEは説明のために書いてあるだけです。中断することがサービスで意味を持たなければ、 サポートしないでください。dwCheckPointdwWaitHint、 および終了コード フィールドは0に初期化します。

次に、 MyServiceStartは、 MyServiceをサービスのHandler関数として登録し、 初期化を開始するためにRegisterServiceCtrlHandlerを呼び出します。次に示す初期化の例のMyServiceInitializationは説明のためだけに含まれています。ここでは、 追加のスレッドを作成するなどの初期化の処理は実行しません。サービスの初期化が1秒以上かかる処理を実行することが予測される場合は、 コード中で定期的にSetServiceStatusを呼び出して待機ヒントと進行状況を示すチェック ポイントを送らなければなりません。

初期化が正常に完了したら、 SERVICE_RUNNING状態を指定してSetServiceStatusを呼び出し、 サービスは仕事を続けられます。初期化でエラーが発生した場合は、 MyServiceStartSERVICE_STOPPEDを指定してSetServiceStatus呼び出しで報告し、 戻ります。

この例では何もしないので、 MyServiceStartは呼び出し側に制御を戻すだけです。しかし、 実際のサービスは、 機能するように設計された動作をこのスレッドを使って実行します。RPC要求を処理するだけのような、 仕事をするスレッドを持つ必要のないサービスのServiceMain関数は、 呼び出し側に制御を戻さなければなりません。ExitThread関数を呼び出すよりも、 戻ることのほうが重要です。これは、 戻ることによって引数のために割り当てられたメモリが開放されるからです。

デバッグ情報を出力するためMyServiceStartSvcDebugOutを呼び出します。SvcDebugOutのソース コードは「Win32のサービス プロセスのmain関数の作成」の節にリストされています。

void MyServiceStart (DWORDargc, LPTSTR*argv)

{
DWORDstatus;
DWORDspecificError;

MyServiceStatus.dwServiceType= SERVICE_WIN32;
MyServiceStatus.dwCurrentState= SERVICE_START_PENDING;
MyServiceStatus.dwControlsAccepted= SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_PAUSE_CONTINUE;
MyServiceStatus.dwWin32ExitCode= 0;
MyServiceStatus.dwServiceSpecificExitCode = 0;
MyServiceStatus.dwCheckPoint= 0;
MyServiceStatus.dwWaitHint= 0;


MyServiceStatusHandle = RegisterServiceCtrlHandler(
TEXT("MyService"),
MyServiceCtrlHandler);

if (MyServiceStatusHandle == (SERVICE_STATUS_HANDLE)0) {
SvcDebugOut(" [MY_SERVICE] RegisterServiceCtrlHandler
failed %d\n", GetLastError());
return;
}

// Initialization code goes here.
status = MyServiceInitialization(argc,argv, &specificError);

// Handle error condition
if (status != NO_ERROR) {
MyServiceStatus.dwCurrentState= SERVICE_STOPPED;

MyServiceStatus.dwCheckPoint= 0;
MyServiceStatus.dwWaitHint= 0;
MyServiceStatus.dwWin32ExitCode= status;
MyServiceStatus.dwServiceSpecificExitCode = specificError;

SetServiceStatus (MyServiceStatusHandle, &MyServiceStatus);
return;
}

// Initialization complete - report running status
MyServiceStatus.dwCurrentState= SERVICE_RUNNING;
MyServiceStatus.dwCheckPoint= 0;
MyServiceStatus.dwWaitHint= 0;


if (!SetServiceStatus (MyServiceStatusHandle, &MyServiceStatus)) {
status = GetLastError();
SvcDebugOut(" [MY_SERVICE] SetServiceStatus error
%ld\n",status);
}

// This is where the service does its work.//
SvcDebugOut(" [MY_SERVICE] Returning the Main Thread \n",0);

return;
}

/* stub initialization function */
DWORD MyServiceInitialization(DWORDargc, LPTSTR*argv,
DWORD *specificError)
{
argv;
argc;
specificError;
return(0);
}

制御ハンドラ関数の作成

次の例のMyServiceCtrlHandler関数は、 サービスのHandler関数の例です。 この関数がディスパッチャ スレッドから呼ばれると、 Opcodeパラメータで渡された制御コードを処理し、 サービスの状態を更新するためにSetServiceStatus関数を呼び出します。Handler関数が制御コードを受け取るたびに、 サービスが制御を操作するかどうかにかかわらず、 SetServiceStatus呼び出しで状態を戻すことが適切です。

Handler関数は、 ディスパッチャ スレッドのコンテキストで実行します。したがって、 この関数は時間のかかる操作を行ってはなりません。停止要求などのように長い時間のかかる操作の場合は、 Handler関数はSetServiceStatus呼び出しでSERVICE_STOP_PENDINGメッセージを送り、 必要ならば、 停止操作が進行中であることをほかのサービスに知らせなければなりません。そして、 関数はディスパッチャがそれ以上の要求をサービスできるように戻らなければなりません。ほかのサービス スレッドの1つがその後の待機ヒントと最後のSERVICE_STOPPEDメッセージを送る責任を持ちます。

中断制御を受け取ると、 MyServiceCtrlHandlerSERVICE_STATUS構造体のdwCurrentStateフィールドをSERVICE_PAUSEDにセットするだけであることに注意してください。同様に、 継続制御を受け取ると状態はSERVICE_RUNNINGにセットされます。MyServiceCtrlHandlerは中断および継続制御を処理する正確な例ではないことに注意してください。MyServiceCtrlHandlerHandler関数のテンプレートであり、 中断と継続の制御用のコードはテンプレートの形を整える目的だけで含まれています。実際に中断や継続の制御をサポートするサービスは、 それらのサービスで意図されたように制御を処理しなければなりません。多くのサービスでは、 中断制御も継続制御もサポートしません。サービスが中断も継続もサポートしないことをdwControlsAcceptedパラメータで示すと、 サービス制御マネージャは、 以後はサービスのHandler関数に中断や継続の制御を送らなくなります。

時間のかかるサービスへの停止要求を処理する場合、 現在のスレッドはほかのスレッドを作成するか、 プロセスの停止処理を既存のほかのスレッドに頼みます。現在のスレッドはSERVICE_STOP_PENDINGメッセージをセットしてSetServiceStatusを呼び出して、 呼び出し側に戻るだけです。

デバッグ情報を出力するのに、 MyServiceCtrlHandler関数はSvcDebugOutを呼び出します。SvcDebugOutのソース コードは「Win32のサービス プロセスのmain関数の作成」の節にリストされています。

VOID MyServiceCtrlHandler ( INDWORDOpcode)
{

DWORDstatus;

switch(Opcode) {
case SERVICE_CONTROL_PAUSE:
/* Do whatever it takes to pause here.
MyServiceStatus.dwCurrentState = SERVICE_PAUSED;
break;

case SERVICE_CONTROL_CONTINUE:
/* Do whatever it takes to continue here.
MyServiceStatus.dwCurrentState = SERVICE_RUNNING;
break;

case SERVICE_CONTROL_STOP:
/* Do whatever it takes to stop here.

MyServiceStatus.dwWin32ExitCode = 0;
MyServiceStatus.dwCurrentState= SERVICE_STOPPED;
MyServiceStatus.dwCheckPoint= 0;
MyServiceStatus.dwWaitHint= 0;

if (!SetServiceStatus (MyServiceStatusHandle, &MyServiceStatus))
{
status = GetLastError();
SvcDebugOut(" [MY_SERVICE] SetServiceStatus error
%ld\n",status);
}

SvcDebugOut(" [MY_SERVICE] Leaving MyService \n",0);
return;

case SERVICE_CONTROL_INTERROGATE:

/* fall through to send current status
break;

default:
SvcDebugOut(" [MY_SERVICE] Unrecognized opcode %ld\n", Opcode);
}

/* Send current status.
if (!SetServiceStatus (MyServiceStatusHandle,&MyServiceStatus)) {
status = GetLastError();
SvcDebugOut(" [MY_SERVICE] SetServiceStatus error
%ld\n",status);
}
return;
}

SCManagerデータベースのオープン

以下に示す例では、 SCManagerデータベース オブジェクトのハンドルをオープンしなければなりません。サービス制御マネージャ データベースに対するさまざまな操作にはさまざまなレベルのアクセスが必要ですが、 必要最小限のアクセスだけを要求してください。SC_MANAGER_ALL_ACCESSを要求すると、 呼び出し側プロセスが管理者特権を持っていなければ、 OpenScManager関数は失敗します。次の例は、 ローカル マシンのServicesActiveデータベースへの完全なアクセスを要求する方法を示しています。

/* Open a handle to the SC Manager database. */

schSCManager = OpenSCManager(
NULL,/* local machine*/
NULL,/* ServicesActive database */
SC_MANAGER_ALL_ACCESS); /* full access rights*/

if (schSCManager == NULL)
MyErrorExit("OpenSCManager");
 

サービスのインストール

サービス構成プログラムは、 CreateService関数を使って、 サービス制御マネージャ データベースにサービスをインストールします。schSCManagerハンドルは、 SCManagerオブジェクトへのSC_MANAGER_CREATE_SERVICEアクセスを持っていなければなりません。次に示す例は、 サービスのインストール方法を示しています。

VOID CreateSampleService() {
LPCTSTR lpszBinaryPathName =
TEXT("%SystemRoot%\\system\\testserv.exe");

schService = CreateService(
schSCManager,/* SCManager database*/
TEXT("Sample_Srv"),/* name of service*/
SERVICE_ALL_ACCESS,/* desired access*/
SERVICE_WIN32_OWN_PROCESS, /* service type*/
SERVICE_DEMAND_START,/* start type*/
SERVICE_ERROR_NORMAL,/* error control type*/
lpszBinaryPathName,/* service's binary*/
NULL,/* no load ordering group */
NULL,/* no tag identifier*/
NULL,/* no dependencies*/
NULL,/* LocalSystem account*/
NULL);/* no password*/

if (schService == NULL)
MyErrorExit("CreateService");
else
printf("CreateService SUCCESS\n");

CloseServiceHandle(schService);

}
 

サービスの削除

次の例のサービス構成プログラムは、 OpenService関数を使って、 インストールされているサービス オブジェクトへのDELETEアクセスを持つハンドルを取得します。プログラムは、 DeleteService関数にこのサービス オブジェクト ハンドルを指定して、 サービス制御マネージャ データベースからサービスを削除します。

VOID DeleteSampleService() {

schService = OpenService(
schSCManager,/* SCManager database*/
TEXT("Sample_Srv"), /* name of service*/
DELETE);/* only need DELETE access */

if (schService == NULL)
MyErrorExit("OpenService");

if (! DeleteService(schService) )
MyErrorExit("DeleteService");
else
printf("DeleteService SUCCESS\n");

CloseServiceHandle(schService);

}
 

サービスの構成の変更

次の例のサービス構成プログラムは、 ChangeServiceConfig関数を使って、 インストールされているサービスの構成パラメータを変更します。プログラムは、 まず、 データベースをロックして、 構成中のサービスをサービス制御マネージャが起動しないようにします。データベースを正常にロックできれば、 プログラムは、 サービス オブジェクトのハンドルをオープンして構成を変更し、 データベースをアンロックして、 サービス オブジェクト ハンドルをクローズします。データベースをロックできなければ、 QueryServiceLockStatus関数を使って、 ロックに関する情報を取得します。

VOID ReconfigureSampleService(BOOL fDisable) {

SC_LOCK sclLock;
LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf;
DWORD dwBytesNeeded, dwStartType;

/* Need to acquire database lock before reconfiguring. */

sclLock = LockServiceDatabase(schSCManager);

/* If the database cannot be locked, report the details. */

if (sclLock == NULL) {

/* Exit if the database is not locked by another process. */

if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED)
MyErrorExit("LockServiceDatabase");

/* Allocate a buffer to get details about the lock. */

lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS) LocalAlloc(
LPTR, sizeof(QUERY_SERVICE_LOCK_STATUS)+256);
if (lpqslsBuf == NULL)
MyErrorExit("LocalAlloc");

/* Get and print the lock status information. */

if (! QueryServiceLockStatus(
schSCManager,
lpqslsBuf,
sizeof(QUERY_SERVICE_LOCK_STATUS)+256,
&dwBytesNeeded) )
MyErrorExit("QueryServiceLockStatus");

if (lpqslsBuf->fIsLocked)
printf("Locked by: %s, duration: %d seconds\n",
lpqslsBuf->lpLockOwner,
lpqslsBuf->dwLockDuration);
else
printf("No longer locked\n");

LocalFree(lpqslsBuf);
MyErrorExit("Could not lock database");
}

/* The database is locked, so it is safe to make changes. */

/* Open a handle to the service. */

schService = OpenService(
schSCManager,/* SCManager database */
TEXT("Sample_Srv"),/* name of service*/
SERVICE_CHANGE_CONFIG); /* need CHANGE access */
if (schService == NULL)
MyErrorExit("OpenService");

dwStartType = (fDisable) ? SERVICE_DISABLED :
SERVICE_DEMAND_START;

if (! ChangeServiceConfig(
schService,/* handle of service*/
SERVICE_NO_CHANGE, /* service type: no change*/
dwStartType,/* *change* service start type*/
SERVICE_NO_CHANGE, /* error control: no change*/
NULL,/* binary path: no change*/
NULL,/* load order group: no change*/
NULL,/* tag ID: no change*/
NULL,/* dependencies: no change*/
NULL,/* account name: no change*/
NULL) )/* password: no change*/
MyErrorExit("ChangeServiceConfig");
else
printf("ChangeServiceConfig SUCCESS\n");

/* Release the database lock. */

UnlockServiceDatabase(sclLock);

/* Close the handle to the service. */

CloseServiceHandle(schService);

}
 

サービスの構成の問い合わせ

次の例のサービス構成プログラムは、 OpenService関数を使って、 インストールされているオブジェクトへのSERVICE_QUERY_CONFIGアクセスを持つハンドルを取得します。それから、 そのサービス オブジェクト ハンドルをQueryServiceConfig関数に指定して、 サービスの現在の構成を取得します。

VOID GetSampleServiceConfig() {

LPQUERY_SERVICE_CONFIG lpqscBuf;
DWORD dwBytesNeeded;

/* Open a handle to the service. */

schService = OpenService(
schSCManager,/* SCManager database*/
TEXT("Sample_Srv"),/* name of service*/
SERVICE_QUERY_CONFIG);/* need QUERY access*/
if (schService == NULL)
MyErrorExit("OpenService");

/* Allocate a buffer for the information configuration. */

lpqscBuf = (LPQUERY_SERVICE_CONFIG) LocalAlloc(
LPTR, 4096);
if (lpqscBuf == NULL)
MyErrorExit("LocalAlloc");

/* Get and print the information configuration. */

if (! QueryServiceConfig(
schService,
lpqscBuf,
4096,
&dwBytesNeeded) )
MyErrorExit("QueryServiceConfig");

printf("\nSample_Srv configuration: \n");
printf("Type: 0x%x\n", lpqscBuf->dwServiceType);
printf("Start Type: 0x%x\n", lpqscBuf->dwStartType);
printf("Err Control: 0x%x\n", lpqscBuf->dwErrorControl);
printf("Binary path: %s\n", lpqscBuf->lpBinaryPathName);
if (lpqscBuf->lpLoadOrderGroup != NULL)
printf("Load order group: %s\n",
lpqscBuf->lpLoadOrderGroup);
if (lpqscBuf->dwTagId != 0)
printf("Tag ID: %d\n", lpqscBuf->dwTagId);
if (lpqscBuf->lpDependencies != NULL)
printf("Dependencies: %s\n", lpqscBuf->lpDependencies);
if (lpqscBuf->lpServiceStartName != NULL)
printf("Start Name: %s\n",
lpqscBuf->lpServiceStartName);

LocalFree(lpqscBuf);

}
 

サービスの起動

次の例は、 サービスを起動するため、 インストールされているデータベースのハンドルを取得してから、 そのハンドルを指定してStartService関数を呼び出します。StartServiceWin32サービスとドライバ サービスの両方を起動できますが、 この例では、 Win32サービスが起動されると仮定しています。サービスを起動したら、 QueryServiceStatus関数が返したSERVICE_STATUS構造体のメンバを使って、 Win32サービスの進行状態を追跡します。

VOID StartSampleService() {
SERVICE_STATUS ssStatus;
DWORD dwOldCheckPoint;

schService = OpenService(
schSCManager,/* S. C. Manager database */
TEXT("Sample_Srv"),/* name of service*/
SERVICE_ALL_ACCESS);

//SERVICE_START |/* need START access*/
//SERVICE_QUERY_CONFIG); /* and QUERY access*/

if (schService == NULL)
MyErrorExit("OpenService");

if (! StartService(schService, /* handle of service*/
0,/* number of arguments */
NULL) )/* no arguments*/
MyErrorExit("StartService");
else
printf("Service start pending\n");

/* Check the status until the service is running. */

if (! QueryServiceStatus(
schService,/* handle of service*/
&ssStatus) )/* address of status info*/
MyErrorExit("QueryServiceStatus");

while (ssStatus.dwCurrentState != SERVICE_RUNNING) {

/* Save the current checkpoint. */

dwOldCheckPoint = ssStatus.dwCheckPoint;

/* Wait for the specified interval. */

Sleep(ssStatus.dwWaitHint);

/* Check the status again. */

if (! QueryServiceStatus(
schService,/* handle of service*/
&ssStatus) )/* address of status info*/
break;

/* Break if the checkpoint has not been incremented. */

if (dwOldCheckPoint >= ssStatus.dwCheckPoint)
break;

}

if (ssStatus.dwCurrentState == SERVICE_RUNNING)
printf("StartService SUCCESS\n");
else {
printf("\nService not started: \n");
printf("Current State: %d\n",
ssStatus.dwCurrentState);
printf("Exit Code: %d\n", ssStatus.dwWin32ExitCode);
printf("Service Specific Exit Code: %d\n",
ssStatus.dwServiceSpecificExitCode);
printf("Check Point: %d\n", ssStatus.dwCheckPoint);
printf("Wait Hint: %d\n", ssStatus.dwWaitHint);
}

CloseServiceHandle(schService);

}
 

サービスへの制御要求の送信

次の例は、 ControlService関数を使って、 動作中のサービスに制御コードを送ります。各制御コードには、 それぞれ異なったレベルのサービス オブジェクトへのアクセスが必要です。たとえば、 SERVICE_CONTROL_STOPコードを送るには、 サービス オブジェクト ハンドルにSERVICE_STOPアクセスがなければなりません。ControlServiceは、 サービスの最新の状態情報をSERVICE_STATUS構造体に返します。

VOID ControlSampleService(DWORD fdwControl) {

SERVICE_STATUS ssStatus;
DWORD fdwAccess;

/* The required service object access depends on the control. */

switch (fdwControl) {
case SERVICE_CONTROL_STOP:
fdwAccess = SERVICE_STOP;
break;

case SERVICE_CONTROL_PAUSE:
case SERVICE_CONTROL_CONTINUE:
fdwAccess = SERVICE_PAUSE_CONTINUE;
break;

case SERVICE_CONTROL_INTERROGATE:
fdwAccess = SERVICE_INTERROGATE;
break;

default:
fdwAccess = SERVICE_INTERROGATE;
}

/* Open a handle to the service. */

schService = OpenService(
schSCManager,/* SCManager database */
TEXT("Sample_Srv"),/* name of service*/
fdwAccess);/* specify access*/
if (schService == NULL)
MyErrorExit("OpenService");

/* Send a control code to the service. */

if (! ControlService(
schService,/* handle of service*/
fdwControl,/* control code to send*/
&ssStatus) )/* address of status info*/
MyErrorExit("ControlService");

/* Print the service status. */

printf("\nStatus of Sample_Srv: \n");
printf("Service Type: 0x%x\n", ssStatus.dwServiceType);
printf("Current State: 0x%x\n", ssStatus.dwCurrentState);
printf("Controls Accepted: 0x%x\n",
ssStatus.dwControlsAccepted);
printf("Exit Code: %d\n", ssStatus.dwWin32ExitCode);
printf("Service Specific Exit Code: %d\n",
ssStatus.dwServiceSpecificExitCode);
printf("Check Point: %d\n", ssStatus.dwCheckPoint);
printf("Wait Hint: %d\n", ssStatus.dwWaitHint);

return;
}
 

関数

サービス自身、 またはサービスを制御したり構成したりするプログラムが使用するWin32関数を次に示します。

ChangeServiceConfig

CloseServiceHandle

ControlService

CreateService

DeleteService

EnumDependentServices

EnumServicesStatus

GetServiceDisplayName

GetServiceKeyName

Handler

LockServiceDatabase

NotifyBootConfigStatus

OpenSCManager

OpenService

QueryServiceConfig

QueryServiceLockStatus

QueryServiceObjectSecurity

QueryServiceStatus

RegisterServiceCtrlHandler

ServiceMain

SetServiceBits

SetServiceObjectSecurity

SetServiceStatus

StartService

StartServiceCtrlDispatcher

UnlockServiceDatabase

▲ページトップに戻る

【本を読んでもよくらからない】 … 個別指導でわかりやすくお教えします
inserted by FC2 system