タスクスケジューラによるアプリ起動の自動化

いつも改造しているTwitter APIを用いたアプリで，自動起動の機能を追加した際に，タスクスケジューラが便利だったものの，設定方法にてこづりました．今回はその話題．
（前回は[デスクいっぱいに伸ばすウィンドウについて](https://qiita.com/HexagramNM/items/8d7633c8c2f340402ba2)）

# そもそもタスクスケジューラとは
Windowsに標準で入っている自動でソフトなどを起動する機能です．Windows 10なら「スタート」→「Windows管理ツール」→「タスク スケジューラ」の順でたどれば起動できるはずです．特定の時刻になったら起動，一定時間間隔で自動的に起動，あるイベントが発生したら起動など，細かく設定できます．なので，おうちに常に稼働しているデスクトップさえあれば，この設定をすることでTwitterのbotなども簡単に作ることができます（ツイートを取得して，返信する処理を一定時間間隔で起動すればよいので）．ちなみに，GUIはこんな感じです．
<img width="587" alt="タスクスケジューラ.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/152791/1d43a470-f050-cc1c-965c-c80639eac13d.png">

# 今回やりたかったこと
Twitter APIのソフトを作ったは良いものの，ショートカットをダブルクリックして起動するのが面倒くさいので，その起動を自動化したいと思いました．Twitter APIによるデータの取得があるので，**ネットにつながったら自動的に起動させる**ようにしたかったです．

# コマンドプロンプトによるタスク登録
もちろん，上のGUIでもタスクスケジューラにタスクを登録，削除することができますが，コマンドプロンプトからもタスクの登録や削除などを行うことができます．（C#にはタスクスケジューラ用のクラスも用意されているそうですが，登録後にその削除をプログラムでできるか不明だったので，今回はコマンドプロンプトをたたく方法を採用しました．）そのコマンドが**schtasks**コマンドです．最初の引数で作成か，削除かなどの指定が可能です．以下は`schtasks /?`を実行した際に出てくる最初の引数説明の要約です．

| 最初の引数 | 内容 |
|:----------|:----|
| /create | タスクの作成 |
| /delete | タスクの削除 |
| /query | タスクの表示 |
| /change | タスクのプロパティ変更 |
| /run | タスクの実行 |
| /end | 実行中のタスクの停止 |
| /showsid | タスクに対応するセキュリティIDの表示 |
| /? | ヘルプの表示 |

ぶっちゃけ，今回使ったのは`\create`と`\delete`と`\query`くらいでした．各引数についてのさらなる詳細については，`schtasks /create /?`のようにヘルプを出せば見ることができます．（それを扱った[記事](https://qiita.com/minamijoyo/items/8cdb1b3659c703f60969)も存在します．）

# ネットに接続したときというイベントの設定方法
最初はGUIのほうで設定しました．[このサイト](https://www.500yen.com/archives/298)にもありますが，
トリガーをイベント時にして，細かい設定については

* ログ→"Microsoft-Windows-NetworkProfile/Operational"
* ソース→"Microsoft-Windows-NetworkProfile"
* イベントID→10000

にするようです．

# コマンドプロンプトでのイベントトリガー設定方法
コマンドのヘルプを見ると，イベントトリガーを設定するには

`schtasks /create /tn "タスク名" /tr "実行ファイルパス名" /sc onevent /ec Microsoft-Windows-NetworkProfile/Operational /mo "???"`

にすればよいようですが，修飾子（"???"の部分）の指定が分かりません．（本来はフィルタという形でxml形式で指定するそうです．）そこで，GUIで登録したタスクの情報を使います．`schtasks /query /xml /tn "タスク名"`でxmlの状態で出力できるので，これをリダイレクトでファイルに書き込んで確認しましょう．今回は`*[System[Provider[@Name='Microsoft-Windows-NetworkProfile'] and EventID=10000]]`を/moの後ろに入れればよいです．（他のイベントの場合も*[System...の部分を確認しましょう．）（[参考](http://forsenergy.com/ja-jp/taskscheduler/html/0779526d-bdf2-4b92-a504-1bc33905fa60.htm)）

# C#での実行
コマンドプロンプトでのコマンドが分かれば，System.Diagnostics.Processクラスを用いて実行できます．こうして組んだプログラムはこちらになります（[参考](http://home.a00.itscom.net/hatada/csharp/process02.html)）．

```C#
using System.Diagnostics;
using System.Reflection;
using System.Windows
ProcessStartInfo psInfo = new ProcessStartInfo()
            {
                FileName = "schtasks",
                Arguments = "/query /tn AutoTask",
                CreateNoWindow = true,
                UseShellExecute = false
            };
            Process p = Process.Start(psInfo);
            p.WaitForExit();
            int iExitCode = p.ExitCode;
           
            if (!(iExitCode == 0))
            {
                //登録されていない場合は登録する
                psInfo = new ProcessStartInfo()
                {
                    FileName = "schtasks",
                    Arguments = "/create /tn AutoTask /tr \"" + Assembly.GetExecutingAssembly().Location 
                    + "\" /sc onevent /ec Microsoft-Windows-NetworkProfile/Operational /mo "
                    + "\"*[System[Provider[@Name=\'Microsoft-Windows-NetworkProfile\'] and EventID=10000]]\" /F",
                    CreateNoWindow = true,
                    UseShellExecute = false
                };
                p = Process.Start(psInfo);
                p.WaitForExit();
                iExitCode = p.ExitCode;
                if (iExitCode == 0)
                {
                    MessageBox.Show("自動起動の登録をしました．");
                    autoCheck.IsChecked = true;
                }
                else
                {
                    MessageBox.Show("自動起動の登録に失敗しました．");
                }
            }
```

これをGUIのイベントハンドラあたりに書けば，自動起動の登録ができるようになります．schtasksの/Fオプションで強制実行させることができます．（/createの時はいるのか不明ですが，タスクの自動起動を解除する場合で，/deleteの時は，/Fがないと削除確認の状態になり，プログラムが止まってしまうのでつける必要があります．）

# まとめ
今回，タスクスケジューラを用いて，ネットワーク接続時に起動するプログラムを作成しましたが，様々なイベントにタスクスケジューラが対応できるので，いろいろな使い方ができるかと思います．