[Windowsサービス] SESSIONCHANGE時のWTS_〇〇〇達がどういうタイミングで来るのかを調べる

サービス関連 目次

https://tera1707.com/entry/2022/02/06/144447#WindowsService

**Windowsのサービス**をC++で作る際にわからないことがいろいろあったので調べたのだが、まずどこを/何を正しいとして作ればよいのかがわからなかった。

調べてばかりで筆が進まず、が嫌だったのでとりあえず作ってみて動きから作り方を調べようと思い、いろいろ試した。その時のメモ。

---

# やりたいこと

以前、C++でサービスを作成した。 → [以前の記事](https://qiita.com/tera1707/items/dc24ac78ae76906abb83)

そのサービスを使って、ユーザーがログイン(サインイン)したときに何かする、とか、FUS(Fast User Switch)したときになにかする、とかをしたかったので、**「サービスコントロール」**という、なにかイベントが起きたときにサービスに飛んでくるもの(Win32デスクトップアプリでいうところのWM_〇〇〇みたいなイメージ)を使って、処理を行おうと考えた。

しかし、**「SERVICE_CONTROL_SESSIONCHANGE」**というサービスコントロールが来た時の**「dwEventType」**に載ってきている**「WTS_〇〇〇」**というパラメータを見れば、ログインしたのかログアウトしたのかを見れるっぽい、というところまでは分かったのだが、dwEventTypeの一覧が載っているMSページを見ると、ログイン/ログアウト時に来そうなWTS_〇〇が複数あり、どれを使えばやりたいことができるのかがわからなかった。

MS公式

https://docs.microsoft.com/en-us/windows/win32/termserv/wm-wtssession-change#parameters

その内容

```
セッション状態変更通知が送信された理由を説明するステータスコード。このパラメーターは、次のいずれかの値になります。

WTS_CONSOLE_CONNECT（0x1）
lParamによって識別されたセッションは、コンソール端末またはRemoteFXセッションに接続されていました。

WTS_CONSOLE_DISCONNECT（0x2）
lParamによって識別されたセッションは、コンソール端末またはRemoteFXセッションから切断されました。

WTS_REMOTE_CONNECT（0x3）
lParamによって識別されたセッションがリモート端末に接続されました。

WTS_REMOTE_DISCONNECT（0x4）
lParamによって識別されたセッションがリモート端末から切断されました。

WTS_SESSION_LOGON（0x5）
ユーザーがlParamで識別されるセッションにログオンしました。

WTS_SESSION_LOGOFF（0x6）
ユーザーがlParamによって識別されたセッションからログオフしました。

WTS_SESSION_LOCK（0x7）
lParamによって識別されたセッションはロックされています。

WTS_SESSION_UNLOCK（0x8）
lParamによって識別されたセッションのロックが解除されました。

WTS_SESSION_REMOTE_CONTROL（0x9）
lParamによって識別されたセッションは、リモート制御ステータスを変更しました。ステータスを確認するには、GetSystemMetricsを呼び出し、SM_REMOTECONTROLメトリックを確認します。

WTS_SESSION_CREATE（0xA）
将来の使用のために予約されています。

WTS_SESSION_TERMINATE（0xB）
将来の使用のために予約されています。
```

これだけ見た段階では、ログイン時に来そうなのはWTS_SESSION_LOGONだが、名前的にWTS_REMOTE_CONNECTとかも来そう？に見えた。

で、実際動かすと、ログオンするだけでも同時に複数のWTS_〇〇が来るため、何がなんだかわからなくなったので、**[行った操作]**と**[その時に来たWTS_〇〇]**を、時系列に並べるなどして、なにがいつ来るのかはっきりさせたい。

# 時系列に操作と来たWTS_〇〇を並べた

[以前作ったC++サービス](https://qiita.com/tera1707/items/dc24ac78ae76906abb83)を使って、下記をログ出力した。

- SERVICE_CONTROL_SESSIONCHANGEが来た時の
    - dwEventTypeの値
    - それにかかわるSessionIDの値

試しに取ってみたのが下記のログ。
※「admin」「bbb」「ccc」の3人のユーザーを作成し、ログイン/ログアウト等の操作をした。
※「admin」だけが管理者ユーザー。

```
13:54:10 再起動(admin)
13:57:15 サインイン(admin)
4952  2021/12/01 13:57:46 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 13:57:46  WTS_SESSION_UNLOCK
4952  2021/12/01 13:57:46   SessionID:1
13:58:15 ロック(admin)
4952  2021/12/01 13:58:15 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 13:58:15  WTS_SESSION_LOCK
4952  2021/12/01 13:58:15   SessionID:1
13:58:30 サインイン(admin)
4952  2021/12/01 13:58:32 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 13:58:32  WTS_SESSION_UNLOCK
4952  2021/12/01 13:58:32   SessionID:1
13:59:30 サインアウト(admin) ⇒ログイン画面の壁紙が標準？のものだった。(win11のヒラヒラ？のやつ)
4952  2021/12/01 13:59:33 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 13:59:33  WTS_SESSION_LOGOFF
4952  2021/12/01 13:59:33   SessionID:1
4952  2021/12/01 13:59:33 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 13:59:33  WTS_CONSOLE_DISCONNECT
4952  2021/12/01 13:59:33   SessionID:1
4952  2021/12/01 13:59:33 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 13:59:33  WTS_CONSOLE_CONNECT
4952  2021/12/01 13:59:33   SessionID:2
14:02:30 サインイン(admin)
4952  2021/12/01 14:02:32 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:02:32  WTS_SESSION_LOGON
4952  2021/12/01 14:02:32   SessionID:2
14:04:00 サインアウト(admin)
4952  2021/12/01 14:04:04 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:04:04  WTS_SESSION_LOGOFF
4952  2021/12/01 14:04:04   SessionID:2
4952  2021/12/01 14:04:04 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:04:04  WTS_CONSOLE_DISCONNECT
4952  2021/12/01 14:04:04   SessionID:2
4952  2021/12/01 14:04:04 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:04:04  WTS_CONSOLE_CONNECT
4952  2021/12/01 14:04:04   SessionID:3
14:05:00 サインイン(bbb)
4952  2021/12/01 14:05:19 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:05:19  WTS_SESSION_LOGON
4952  2021/12/01 14:05:19   SessionID:3
14:06:00 サインアウト(bbb)
4952  2021/12/01 14:06:06 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:06:06  WTS_SESSION_LOGOFF
4952  2021/12/01 14:06:06   SessionID:3
4952  2021/12/01 14:06:07 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:06:07  WTS_CONSOLE_DISCONNECT
4952  2021/12/01 14:06:07   SessionID:3
4952  2021/12/01 14:06:07 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:06:07  WTS_CONSOLE_CONNECT
4952  2021/12/01 14:06:07   SessionID:4
14:06:30 サインイン(bbb)
4952  2021/12/01 14:06:49 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:06:49  WTS_SESSION_LOGON
4952  2021/12/01 14:06:49   SessionID:4
14:07:40 bbbサインイン中にadmin選択(FUS?) ⇒ログイン画面に戻る
4952  2021/12/01 14:07:41 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:07:41  WTS_CONSOLE_DISCONNECT
4952  2021/12/01 14:07:41   SessionID:4
4952  2021/12/01 14:07:42 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:07:42  WTS_CONSOLE_CONNECT
4952  2021/12/01 14:07:42   SessionID:5
4952  2021/12/01 14:07:42 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:07:42  WTS_SESSION_LOCK
4952  2021/12/01 14:07:42   SessionID:4
14:08:30 サインイン(admin) ⇒ スタートメニューで「bbb signed in」となっている
4952  2021/12/01 14:08:31 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:08:31  WTS_SESSION_LOGON
4952  2021/12/01 14:08:31   SessionID:5
14:10:55 adminサインイン中にccc選択(FUS?) ⇒ログイン画面に戻る
4952  2021/12/01 14:11:21 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:11:21  WTS_CONSOLE_DISCONNECT
4952  2021/12/01 14:11:21   SessionID:5
4952  2021/12/01 14:11:21 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:11:21  WTS_CONSOLE_CONNECT
4952  2021/12/01 14:11:21   SessionID:6
4952  2021/12/01 14:11:21 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:11:21  WTS_SESSION_LOCK
4952  2021/12/01 14:11:21   SessionID:5
14:11:45 サインイン(ccc)
4952  2021/12/01 14:11:46 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:11:46  WTS_SESSION_LOGON
4952  2021/12/01 14:11:46   SessionID:6
14:12:30 サインアウト(ccc)
4952  2021/12/01 14:12:52 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:12:52  WTS_SESSION_LOGOFF
4952  2021/12/01 14:12:52   SessionID:6
4952  2021/12/01 14:12:52 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:12:52  WTS_CONSOLE_DISCONNECT
4952  2021/12/01 14:12:52   SessionID:6
4952  2021/12/01 14:12:52 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:12:52  WTS_CONSOLE_CONNECT
4952  2021/12/01 14:12:52   SessionID:7
14:13:15 サインイン(bbb)
4952  2021/12/01 14:13:16 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:13:16  WTS_CONSOLE_DISCONNECT
4952  2021/12/01 14:13:16   SessionID:7
4952  2021/12/01 14:13:16 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:13:16  WTS_CONSOLE_CONNECT
4952  2021/12/01 14:13:16   SessionID:4
4952  2021/12/01 14:13:16 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:13:16  WTS_SESSION_UNLOCK
4952  2021/12/01 14:13:16   SessionID:4
14:14:00 サインアウト(bbb)
4952  2021/12/01 14:14:22 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:14:22  WTS_SESSION_LOGOFF
4952  2021/12/01 14:14:23   SessionID:4
4952  2021/12/01 14:14:23 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:14:23  WTS_CONSOLE_DISCONNECT
4952  2021/12/01 14:14:23   SessionID:4
4952  2021/12/01 14:14:23 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:14:23  WTS_CONSOLE_CONNECT
4952  2021/12/01 14:14:23   SessionID:7
14:14:45 サインイン(admin)
4952  2021/12/01 14:14:46 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:14:46  WTS_CONSOLE_DISCONNECT
4952  2021/12/01 14:14:46   SessionID:7
4952  2021/12/01 14:14:46 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:14:46  WTS_CONSOLE_CONNECT
4952  2021/12/01 14:14:46   SessionID:5
4952  2021/12/01 14:14:46 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:14:46  WTS_SESSION_UNLOCK
4952  2021/12/01 14:14:46   SessionID:5
14:15:00 サインアウト(admin)
4952  2021/12/01 14:15:02 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:15:02  WTS_SESSION_LOGOFF
4952  2021/12/01 14:15:02   SessionID:5
4952  2021/12/01 14:15:03 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:15:03  WTS_CONSOLE_DISCONNECT
4952  2021/12/01 14:15:03   SessionID:5
4952  2021/12/01 14:15:03 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:15:03  WTS_CONSOLE_CONNECT
4952  2021/12/01 14:15:03   SessionID:8
14:16:15 シャットダウン(ログイン画面でシャットダウンを押下)
4952  2021/12/01 14:16:17 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:16:17  WTS_CONSOLE_DISCONNECT
4952  2021/12/01 14:16:17   SessionID:8
4952  2021/12/01 14:16:17 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:16:17  WTS_CONSOLE_CONNECT
4952  2021/12/01 14:16:17   SessionID:7
14:16:35 電源ボタン押して起動？？時間間違えたかも
14:17:30 サインイン(admin)
4952  2021/12/01 14:17:44 SERVICE_CONTROL_SESSIONCHANGE
4952  2021/12/01 14:17:44  WTS_SESSION_LOGON
4952  2021/12/01 14:17:44   SessionID:7
```

これを見ると、こんなタイミングでそれぞれ出てるように見える。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/d4175993-c97e-a414-b141-2d0f9a48621b.png)

表中の上の4つ(LOCK/UNLOCK、LOGON/LOGOFF)は何となくイメージ通りなのだが、
CONSOLE_CONNECTとCONSOLE_DISCONNECTの2つが、名前から想像しにくいタイミングで来ていた。

特に**CONSOLE_CONNECT**は「CONNECT」だからログインしたときに来そうなものだが、**サインアウトしてログイン画面に戻ったとき**に出ていた。これはどういうことなのか？

# 時系列に操作と来たWTS_〇〇をタイミングチャートっぽく並べた

上のログを、わかりやすいように図にした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/57016bcf-49b4-d242-b079-7039d5740f2a.png)

WTS_〇〇をログするときに、一緒に来たセッションIDも記録するようにしていたので、それも合わせて図にした。

多分に推測を含むのだが、動きからすると下記のような動きをしているように見える。

- 「セッションID」は、常時最低1コ存在している。
- ユーザーがログインすると、セッションIDとユーザーが紐づけられる。
- その紐づけは、そのユーザーが「サインアウト」するまで続く。
- サインアウトすると、セッションIDは捨てられ、次のセッションIDができる。(+1されたもの)
- サインアウトではなくFUSして、元のユーザーがサインインしたまま別のユーザーにサインインすると、新しい(+1された)セッションIDが出来はするが、元のセッションIDは捨てられはせず、次に元のユーザーがサインインしたときに、そのセッションIDが使われる。

- サインアウトやFUS、ロック解除をしたとき≒ログイン画面に戻ったとき、に、**「WTS_CONSOLE_DISCONNECT」**が出る。
- FUSで、別のユーザーでサインインした後元のユーザーでサインインしなおしたときにも、**「WTS_CONSOLE_DISCONNECT」**が出る。

# WTS_CONSOLE_DISCONNECTについて

ともかく、図を見ると、
**「WTS_CONSOLE_DISCONNECT」**は、あるユーザーがサインアウトしたりロック(FUSで別ユーザーに切り替わるのもロックに含む)したりするような、なんと表現すればよいか、ユーザーのサインイン状態が**「抜ける」**方向に変化したときに出るっぽい。

(すでにサインイン中のユーザーから、別のサインイン中のユーザーにFUSするときも、サインインなので**「入る」**方向っぽいが、FUS元のユーザーから見たら**「抜ける」**方向になる)

で、その**「抜けた」**ときには、**「WTS_CONSOLE_CONNECT」**がペアで出て、

- すでにサインインしているユーザーのロックを解除する際は、そのユーザーのセッションIDを使いまわす
- それ以外は、新しいセッションIDを新しく作る。(+1する)

となるっぽい。


# 試したいこと

まだ見れてないパターンがある。時間のあるときに、下記を試したい。

- 一人だけサインインしているときに「Lock」すると、どんなWTSがくるか。  
  (一番最初のサインイン→ロック がそれにあたるのだが、SESSIONUNLOCKとLOCKしかでてないのは何かおかしい?ログの取得失敗かもしれない？ので再度確認したい)
- 「WTS_REMOTE_CONNECT」「WTS_REMOTE_DISCONNECT」が見れてない。試したい。

# 資料

エクセル

https://github.com/tera1707/WPF2/blob/main/%E8%B3%87%E6%96%99/211217_%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E5%8B%89%E5%BC%B7.xlsx

ログ

https://github.com/tera1707/WPF2/blob/main/%E8%B3%87%E6%96%99/211217_%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E5%8B%89%E5%BC%B7.txt

# 参考

以前つくったC++サービス

https://qiita.com/tera1707/items/dc24ac78ae76906abb83

WTS_〇〇の値の仕様

https://docs.microsoft.com/en-us/windows/win32/termserv/wm-wtssession-change#parameters

WTS_〇〇を受け取るためのハンドラを登録する関数

https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-registerservicectrlhandlerexa

そのハンドラの仕様

https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nc-winsvc-lphandler_function_ex

↑の[in] dwEventType のところに、
dwControl が SERVICE_CONTROL_SESSIONCHANGE だったら、その時の dwEventType に、 WM_WTSSESSION_CHANGE でいうところの wParam の値と同じものが載っている、と書いてる。
(で、WM_WTSSESSION_CHANGE のページを見たら、WTS_〇〇の一覧が載っている。ややこしい、、、)