デバイスと対話する Win32 サービス
INF AddService を介してインストールされ、デバイスと対話する理想的な Win32 サービスは、ドライバーがデバイスと対話するのと同様に動作します。 ドライバーはデバイスの存在に応じて読み込まれ、アンロードされます。デバイスと対話する Win32 サービスは、デバイスの存在に応じて、この同じパターンの 開始 と 停止 に従う必要があります。
サービスは、デバイス インターフェイスが存在し、対話できるときにのみ開始し、デバイス インターフェイスが有効でなくなったときに停止する必要があります。 この設計パターンにより、望ましくない動作と未定義の動作を最小限に抑える堅牢なサービスが確保されます。 このパターンに従うようにサービスを設計する方法について説明します。
サービスのインストール
サービスをインストールするには、INF AddService ディレクティブを使用します。 これにより、サービスを作成して開始できます。
サービスをデマンド スタートする設定を追加します。 これは、StartType=0x3 (サービスをトリガー スタートする) を設定することによって達成できます。
このセクションの最後の手順では、AddTrigger ディレクティブを使用して、デバイス インターフェイスが到着したときにサービスを開始します (AddTrigger の詳細については、AddService を参照してください)。 AddTrigger の使用方法の例を次に示します。
[UserSvc_Install]
ServiceType = 0x10 ; SERVICE_WIN32_OWN_PROCESS
StartType = 3 ; SERVICE_DEMAND_START
ErrorControl = 0 ; SERVICE_ERROR_IGNORE
ServiceBinary = %13%\oemsvc.exe
AddTrigger = UserSvc_AddTrigger
[UserSvc_AddTrigger]
TriggerType = 1 ; SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL
Action = 1 ; SERVICE_TRIGGER_ACTION_SERVICE_START
SubType = %GUID_DEVINTERFACE_OSRFX2% ; Interface class GUID
DataItem = 2, "USB\VID_0547&PID_1002" ; SERVICE_TRIGGER_DATA_TYPE_STRING
DataItem で指定される HardwareId は省略可能であり、通常は、ジェネリック クラス インターフェイスを使用してトリガーをより具体的なデバイスにスコープ設定する場合にのみ必要であることに注意してください。
サービス ランタイム
ランタイムの観点から、サービスの最初の手順は、デバイス インターフェイス通知の登録です。 これを実現する方法に関する規範的なガイダンスについては、「デバイス インターフェイスの到着とデバイスの削除の通知の登録」のページを参照してください。
特に、CM_Register_Notification を CM_NOTIFY_FILTERY_TYPE_DEVICEINTERFACE フラグと共に使用して、デバイス インターフェイス通知の適切な登録を達成する必要があります。
Note
サービスの開始時には、到着通知が既に通過している可能性があるため、デバイス インターフェイス通知を受信するという事実に依存することはできません。特に、デバイス インターフェイスの到着がサービスの開始の原因である場合です。 代わりに、既にインターフェイスが存在する場合は、チェックするデバイス インターフェイスの一覧を取得する必要があります。
デバイス インターフェイスの通知を登録すると、新しいデバイス インターフェイスが有効になっているか、既存のデバイス インターフェイスが無効になっているかどうかが通知されます。 通知コールバックからデバイス インターフェイス パスを検出できます。 既存のデバイス インターフェイスのリストをクエリして、サービス開始前に存在し、通知が登録されていたデバイス インターフェイスを調べるには、CM_Get_Device_Interface_List などの API を通じて、デバイス インターフェイスのリストを取得できます。
Note
通知を登録してから、システムに既に存在するデバイス インターフェイスのリストを取得するまでの間に、デバイス インターフェイスが到着する可能性があります。 その場合、デバイス インターフェイスは、通知コールバックとデバイス インターフェイスの一覧の両方に一覧表示されます。
I/O API でデバイス インターフェイスと対話する場合、目的のデバイス インターフェイスを見つけたら、CreateFile を介してインターフェイスへのハンドルを開きます。
次の手順では、セカンダリのハンドルごとの通知を登録して、デバイスの削除や削除されるデバイスに関するクエリの試行など、デバイスの状態変更の通知を受け取ります。 これは、CM_Register_Notification を CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE フラグと共に使用して行うことができます。 「デバイス インターフェイスの到着とデバイスの削除の通知の登録」に記載されているガイダンスに従うと、デバイスが削除されるとき、それに応じてハンドルを解放できます。
デバイス インターフェイスの到着と削除を追跡する必要があり、サービスが対話する最後のデバイス インターフェイスを削除すると、サービスを停止できることを意味します。 最後のインターフェイスが削除されたら、サービスを停止します (詳細については、このページを参照してください)。 これを行うには、次の手順に従います。
SERVICE_STOP_PENDING 状態を SCM にポストして、サービスがダウンすることを示します
サービスで使用されていたすべてを初期化解除/クリーンアップします
SERVICE_STOP 状態を SCM にポストして、停止操作を完了します
サービスが停止している場合は、既存のすべての開いているハンドルをチェックし、デバイス インターフェイスに移動し (存在しない場合もあります)、クリーンアップします。
デバイス インターフェイスは、デバイスのインストール中、デバイスの有効化/無効化、デバイスの再列挙、システムの再起動中、または一覧にないその他のシナリオの間に戻ることができます。 デバイス インターフェイスが戻ると、トリガーの開始登録に基づいてサービスがトリガー開始されます。
このフローにより、サービスはデバイス インターフェイスの到着時に開始し、最後のデバイス インターフェイスが存在しなくなったときに停止します。
コード サンプルと関連リンク
GitHub には、サービスがこのイベント フローを活用する方法を説明するサンプルがあります。 サンプルは、Win32 Service Sample にあります。
さらに、AddTrigger に関する有用なドキュメントが AddService ページにあります。