タスクスケジューラーで、定期的に自作exeを起動したい

もくじ
https://tera1707.com/entry/2022/02/06/144447

やりたいこと

ある処理を行うexeを作成したのだが、そのexeを使って、

  • WindowsPCを起動して、
  • ログインした3分後に一度実行したい。
  • さらに、その後10分おきにもexeを起動したい。
  • (exeは、起動してすぐ処理を終えて、プロセス終了するものとする)

ということをしたい。

で、最初、タスクスケジューラーでログイン後にexeを起動させて、そのexeの中でSleep(5分)とかして、さらに無限ループでSleep(10分)、みたいなことをしようとしたが、自前で繰り返し処理をつくらずに、Windowsが持ってるタスクスケジューラーでそういうことができないか、調べてみる。

気にした点

Windowsのタスクスケジューラーを使う上でまず気にしたのが、

  • 繰り返しのタスク起動時に、その起動予定時間をスリープ等でまたいだ時に、スリープ復帰後にちゃんと起動してくれるか?
  • 繰り返しの待ちの間に、スリープしたときに、スリープしていた分、次の起動の時刻が遅れたりしないか?

ということだった。それも気にしながら調べてみた。

結論

色々試した結果、

  • ログイン後、3分遅れでexeを起動させたい
  • その後、10分おきに、exeを起動させたい
  • ちょうど10分経過時に、スリープ等でexe動けないような場合は、スリープ解除時など、動けるようになった時点でexe起動したい

という要求であれば、タスクスケジューラーを下記のように設定すればよいと思われる。

一応、注意としては、「動けるようになった時点でexe起動」は、実際に試したところ、スリープからの復帰時であれば、スリープ復帰から3~5分くらいで、exe起動するようだった。(手持ちのPCで試したところ、1代は3分、もう一台は5分程度かかっていた)

また、今回の要求には問題はないのだが、 「スケジュールされた時刻にタスクを開始できなかった場合、すぐにタスクを実行する」のチェックを外していても、今回の設定だと、スリープ明けに、タスクが実行された。

数回試した限りだが、

  • トリガーを「ログオン時」にして「繰り返し間隔」に入れていると、「スケジュールされた時刻にタスクを開始できなかった場合、すぐにタスクを実行する」にチェックを入れていなくても、スリープ復帰時にタスク実行される
  • トリガーを「スケジュールに従う」「1回」で、繰り返し間隔を入れていると、同チェックを入れていないと、スリープ復帰時にタスクは実行されない

ような感じだった。
(ただ上の図ではそのチェック入れていないが、念のため入れておいた方がよいかもしれない。(動いてほしいので))

あと、「タスクがすでに実行中の場合に適用される規則」は、実行するタスク(exe)が、実行の間隔よりも長くかかる(次の実行時にまだexeが動いたままである)可能性がある場合には、「既存のインスタンスを停止」などを検討したほうがよさそう。(今回それはないのでまだ未検証)

以下、結論にたどり着く前にいろいろ試したときのメモ。

「タスクがすでに実行中の場合に適用される規則」をいろいろ試した

上のように設定した、繰り返し実行されるタスクで、タスクの実行間隔が経過しても終わらないようなexeを起動させたときに、「タスクがすでに実行中の場合に適用される規則」の設定によってどういう動きをするかを試してみた。

試した感触は、想像通りの動き、という感じ。

※前提として、下図のようにタスクを設定した。

exeのコードは下記のようなもの。
(「Thread PID」とか、言ってることおかしいが許してください)

using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Windows;

namespace TeikiJikkouJikken
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        private void Application_Startup(object sender, StartupEventArgs e)
        {
            LogOnDesktop.WriteLogToDesktopLogFile("exe executed.");

            var pid = Process.GetCurrentProcess().Id;

            while (true)
            {
                LogOnDesktop.WriteLogToDesktopLogFile($"  Thread PID : {pid} is running...");
                Thread.Sleep(10000);                
            }

            this.Shutdown();
        }
    }
}

// using System.IO;
static class LogOnDesktop
{
    public static void WriteLogToDesktopLogFile(string line)
    {
        var logPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + @"\log.log";

        File.AppendAllText(logPath, DateTime.Now.ToString("hh:mm:ss.fff") + "  " + line);
        File.AppendAllText(logPath, Environment.NewLine);
    }
}

「既存のインスタンスの停止」を選択

下図のように、一瞬オーバーラップはするようだが、新しくexeが起動すると、前のexeは終わってくれた。(一瞬オーバーラップ、は、場合によっては注意が必要かも。。。)

「新しいインスタンスを開始しない」を選択

単純に、先に起動していたexeがそのまま動き続けた。

「新しいインスタンスを並列で実行」を選択

起動タイミングがくるたびに、新しいexeが起動した。

「新しいインスタンスをキューに追加」を選択

ずっと終わらないexeだと、「新しいインスタンスを開始しない」と同じように、ずっと最初のexeが動いたままになった。

それだと違いがわからないので、実験exeを、90秒で終わるように改造したら、下図のようになった。

→想像通り、前のexeが終わったら、次のexeが始まる。(前のexeが終わったら、キューに入れられてた次のexe(exeというか、次のタスクということか)が始まってる。)

「タスクを停止するまでの時間」をいろいろ試した

下図の画面の「タスクを停止するまでの時間」がどういうことができる設定なのかよくわからなかったので試した。

試した結果を文字にするとそのまんまなのだが、「タスク(exe)を停止させるまでの時間」の設定だった。

この設定のチェックを入れて、時間の設定をすると、その時間にexeが終了した。

UIでは、1時間~3日間の間のとびとびの値しか設定できないが、ComboBoxの中身を編集して「1 分間」としてやると、もっと短い時間でも設定できて、その通りに動いた。(「1 分間」で確認した)

こちらのサイトによると、「この設定は、実行に時間がかかりすぎてユーザーの作業に、支障を及ぼす可能性があるタスクを制限するために使用する。」とのことなので、時間内に終わってくれるようなexeであれば、使う必要のない設定なのだと思われる。

「要求時に実行中のタスクが終了しない場合、タスクを強制的に停止する」をいろいろ試した

この設定が最もどういうことかわからなかったのだが、おそらくこの設定は、「タスクを停止するまでの時間」の設定とセットになる設定だと思われる。

だと思われる、というのは、実験で作った終わらないプロセスが、「タスクを停止するまでの時間」の設定の通りに終わってくれてしまうので、強制的に終わらせないといけないような状況を作れなかったので、「要求時に」というのが停止要求をプロセスにおくるということであれば、実験プロセス側は、停止要求なんてものを受け取るようには作ってないのだが、、、、と思った。

停止要求=taskkill的なものなんだとしたら、この設定の「強制的iに停止する」ってのは何なのだ?とも思う。。。ただ、思うようには動いてくれているので、いったん棚上げすることにする。。。。

注意:設定の「タスクを停止するまでの時間」の精度?には期待しない方がよさそう

勝手な思い込みで、

  • トリガーの「繰り返し間隔」を1分にしているときに、
  • タスク(exe)が1分以内に終わらなくても、
  • 設定の「タスクを停止するまでの時間」も1分にして、
  • 設定の「新しいインスタンスを開始しない」にしてやれば、
  • 1分経過時点でタスクが停止するのだから、新しいインスタンスを開始しなくても次のインスタンスが立ち上がってくれるはず!

と思っていたのだが、立ち上がってはくれなかった。

「タスクを停止するまでの時間」を1分にしていても、「繰り返し間隔」の1分到来のぴったりの時間までにタスクが停止されてくれるわけではなかったなので、 「新しいインスタンスを開始しない」にする場合は、次の繰り返し間隔到来までに、余裕をもって、タスク(exe)を終わらせておく必要がありそう。 (ただ、その場合でも、さらに次の繰り返し間隔が到来したら、タスク(exe)は起動してくれはするようだが、多くの場合は、一回飛ばされると困るはず)

※そもそも、タスクスケジューラーで動かすexeを、そんな長い時間実行するようなものにすな!という話だが、、、

参考

タスクスケジューラーの公式doc

https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-r2-and-2008/cc721871(v=ws.10)

タスクスケジューラのタスクを指定した間隔で定期的に実行する方法

https://cloud-si.com/taskschd-interval/

タスク スケジューラで無期限に繰り返しを行うタスクを作成する際の注意点
→一定時間で繰り返しさせる場合、「1回」にしとかないと、重複して起動するっぽい。(「毎日」とかにしたらダメ?)

https://jpwinsup.github.io/blog/2023/02/07/UserInterfaceAndApps/Repeat-task-indefinitely-issues/

下記ページが、ものすごく詳しい

https://jm1xtk.com/cnt/109_task/index.php

















以下、実験途中のメモなのでまとまりなし & 間違い多し

「要求時に実行中のタスクが終了しない場合、タスクを強制的に停止する」をチェックしてると、時間内(タスク時間到来後、さらに次のタスク時間が来るまで?)にタスクで起動したexeがおわらないと、次から起動しなくなる? (MsgBoxを出しっぱなしにするような実験PGだとそうなる??)

どうやら上記✅OFFしても、次から起動しない。 タスク起動予定時刻をまたいでスリープしたあと、レジュームすると、その後タスク動いてくれない。(前回の実行結果をみると「正しく終了しました」となってるが、動いてない。)

下記が原因か?やってみる。

https://cloud-si.com/taskschd-instance/

↑そういうわけでもなさそう。平行軌道にしても、予定時刻をまたいでると、その次から起動してくれなかった。

まだはっきりとした原因わかってないが、下記のように設定すると、タスク予定時間をまたいだ後も、タスク発動してくれている。

■いけそうな設定①

「スケジュールされた時刻にタスクを開始できなかった場合、すぐにタスクを実行する」のチェックを入れてると、時間をまたいだ後にresumeして、「しばらくすると」タスクが動いてくれるっポイ。
上記設定では、しばらく=3分~5分、位に見える、、、 (家のメインPCでは5分くらい、古いNotePCだと3分くらいだった)

なので、そのチェックを入れると、スリープのresume後「タスクスケジューラーが動けるようになり次第」動いてくれる、という感触。

その場合も、元々動いていた間隔からずれたりはしない。 (例えば、5分間隔で13:45:25に最初起動してて、13:50:25、13:55:25に起動して、その後スリープしたとしたら、スリープ明けも、14:00:25、14:05:25・・・に動く、ということ)