[C#/WPF] ユーザーがログイン/ログアウト、PCをシャットダウン等したときになにかしたい

もくじ
⇒https://qiita.com/tera1707/items/4fda73d86eded283ec4f

# 類似記事

- [[C#/C++] WPFアプリでディスプレイのON/OFFを取得する（そのほか、WM_POWERBROADCASTでサスペンド⇔レジューム等も取れる）](https://qiita.com/tera1707/items/dc4d261fd0d64b8fc725)
- [[C#/WPF] ユーザーがログイン/ログアウト、PCをシャットダウン等したときになにかしたい](https://qiita.com/tera1707/items/e7b4df031b750d0663c6)

# やりたいこと

アプリ起動中に、下記のような操作でユーザーがシャットダウンやログオフなどをしたとき、またその逆のログインしなおしたとき等に、アプリ側でそれを知って、しかるべき処理をしたい。(何かのサーバーやデバイスと通信するようなアプリであれば、一旦通信を終わらせたり、逆に再開したりしたい)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/f12fa614-8881-7178-8f1b-3e10b25b8d34.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/613982dc-2ed6-f276-3447-dd10ec2f2bfe.png)

# やり方
`SystemEvents`にある各種イベントを利用する。
↓この辺を使う。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/d6c84397-1ff9-6d9a-063f-a24ea6933971.png)
→[MS資料](https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.win32.systemevents.powermodechanged?view=dotnet-plat-ext-3.1)

# 実験コード
https://github.com/tera1707/WPF-/tree/master/038_PowerMode_SessionSwitch

```実験コード抜粋.cs
// 何のイベントハンドラが動いたのか、またイベントのArgの中身を表示する。
private void root_Loaded(object sender, RoutedEventArgs e)
{
    SystemEvents.SessionSwitch          += ((sender, e) => { AddLog("SessionSwitch       :" + e.Reason.ToString()); });
    SystemEvents.SessionEnding          += ((sender, e) => { AddLog("SessionEnding       :" + e.Reason.ToString()); });
    SystemEvents.SessionEnded           += ((sender, e) => { AddLog("SessionEnded        :" + e.Reason.ToString()); });
    SystemEvents.PowerModeChanged       += ((sender, e) => { AddLog("PowerModeChanged    :" + e.Mode.ToString()); });
    SystemEvents.EventsThreadShutdown   += ((sender, e) => { AddLog("EventsThreadShutdown:" + e.ToString()); });
}
```

**実験アプリ仕様：**
アプリを起動したまま、シャットダウンやらログオフやらをすると、画面表示＆exeと同じ階層にログを出力してくれる仕組み。（ListBoxにログを出して、なんのイベントが来たか見ようとしたが、それだとシャットダウンやログアウトしたときに実験アプリが終わってしまい、見れないので、ログファイルを残すようにした）

# ロック、再度サインイン

ロックして、再度サインインしたらどうなるか？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/5de36757-17fd-f62a-2ea0-658dded35d79.png)


結果
`SessionSwitch`イベントで、`e.Reason`が`SessionLock` → `SessionUnlock`がきた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/f21ff99b-d627-1b18-75a7-a21ab2e46b1d.png)



# 他のユーザー切り替えして、戻ってくる

他のユーザーに一旦切り替えて、また自分のユーザーに戻ってくる(サインインしなおす)
(＝FUS(Fast User Switching)→あるユーザーがログインしたまま、別のユーザーもログインしてるイメージ)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/d98001bb-3b5b-2026-f2ee-78f25e34976f.png)

結果
`SessionSwitch`イベントで、下記の順番で来た。(下から順番に)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/e76ae280-409a-6dc5-837a-f6209225fb2c.png)
※今回は自分のPCで直接ユーザー切り替え操作を行ったが、もしこれがリモートデスクトップでの接続だったら、`ConsoleConnect`と`ConsoleDisconnect`が、`RemoteConnect`と`RemoteDisconnect`になるっぽい。

# サインアウト(ログアウト、ログオフともいう)

サインアウトする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/cd318091-0e60-652d-4c7d-cafb7029219f.png)

結果
`SessinEnding`の`Logoff`と、そのあとに`SessinInded`の`Logoff`が来た。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/72a396c4-5d5c-f033-2e0f-52d777bbdf31.png)



# スリープ、復帰（サスペンド、レジュームともいう）
スリープさせる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/b0cf9796-08fb-43cc-ad2c-e55e26e14690.png)

結果
下記の順で来た。(下から順番に)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/89a621bc-717f-62bd-44cc-ee648ff71304.png)


# シャットダウン
シャットダウンする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/f75ad6f4-ca3c-3352-37b6-3f25dd92ef76.png)

結果
`SessionEnding`の`SystemShutdown`のあとに、`SessionEnded`の`SystemShutdown`が来た。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/5b7bca2f-35cb-6f4e-64e4-66a76009940b.png)

# C++で似たことを試した

下記記事で、Wndprocにくる「電源イベント」をフックすることで、サスペンド⇔レジューム等のイベントを取るのを試した。

電源イベントではログオフ、ログオンは取れないが、スリープやサスペンド、レジュームは取れるので、似たことができると思う。（試してないけどたぶん、`WM_POWERBROADCAST`じゃなくて違うWMイベントをとれば、セッション系イベントも取れると思う）

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


# 参考

SystemEvents.PowerModeChanged イベント
https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.win32.systemevents.powermodechanged?view=dotnet-plat-ext-3.1

上記ページの左側に、イベントが一覧になってるので、そこから必要なイベントに飛べる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/9edf86e8-4902-19d4-5c31-ce761cdc7642.png)

イベントの引数を見て、詳細な情報(例えば、SessionSwitchであれば、suspendなのか、resumeなのか、など)を知る必要があるが、そのへんは、同じページの下の方に、`〇〇Args`へのリンクが載ってるので、そこから飛んで、引数を調べればいい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/dfd5cd74-6a0b-ec23-e56a-afb5ef84db3f.png)