指定時刻にマクロを実行するには

一般的にマクロの実行はボタンを押すなど自分のタイミングで実行させますが、そうではなく、「10時から」とか「5分後に」などのように指定時刻に実行したい場合があります。

このような場合は、Application.OnTimeメソッドを利用することで指定した時刻に処理を実行することを予約することが出来ます。OnTimeメソッドはあくまでも「実行することの予約」だけを行います。実行予約はすぐに終わるためすぐに次の行の処理を行います。

OnTimeメソッドは分かりやすく言えば、テレビのレコーダーの録画予約みたいなものです。「スケジュール」とか「予約」とか「予定」という言葉が出てきますがどれも同じ意味で使われています。「録画予約」という言葉を「実行予約」とか「実行スケジュール」と言い換えたものと思えばどういうものが想像付きやすいと思います。

OnTimeメソッドでの実行予約はExcelが管理するため、実行予約をしたあとはExcelを起動したままにしておく必要があります


構文

Sub Application.OnTime(EarliestTime, Procedure As String, [LatestTime], [Schedule])

Application
必須
Applicationオブジェクトを指定します。通常は「Application.OnTime」と書きます。
EarliestTime
必須
プロシージャ(関数)を実行する日時を指定します。型は未指定のためVariant型の引数ですが、実際に設定する値はDate型を指定します。

一般的には「TimeValue(“12:34:56”)」のように指定時刻をTimeValue関数でDate型として指定します。

Procedure
必須
プロシージャ名(関数名)を文字列で指定します。

引数を指定する場合は「’TestSub 123, “abcd”」のようなフォーマットで指定します。プロシージャ名から引数までをシングルクォーテーションで囲み、プロシージャ名の後ろに半角スペースを入れて引数を指定します。文字列型の場合はダブルクォーテーションで囲みます。数値型はダブルクォーテーションは使いません。引数が複数ある場合は「”, “」カンマで区切ります。

引数を指定しない場合はシングルクォーテーションで囲む必要はありません。

LatestTime
(省略可)
指定時刻に他のマクロが動作中ですぐに実行できない場合に、実行中のマクロが終わるまでどれぐらい待つかを指定します。

この引数を省略した場合は実行中のマクロが終わるまで待ちます。実行中のマクロが無ければここで指定した待ち時間は無視され、指定時刻に実行されます。

Schedule
(省略可)
TrueかFalseを指定します。省略時はTrueが指定されます。

Trueを指定すると指定時刻に実行することが予定として有効になります。録画予約をした、のような状態です。通常はこれです。

Falseを指定した場合は先に呼び出されたOnTimeメソッドの指定時刻に動作させる予定を取り消します。テレビのレコーダーを例にすると、既に登録済みの録画予約の取り消す場合の操作と思ってもらうと分かりやすいと思います。

戻り値 ありません。

Application.OnTimeメソッドを呼び出さないと実行されない

Windowsでは指定時刻にアプリケーションを実行する仕組みとしてタスクスケジューラがあります。

タスクスケジューラに登録された内容はOSとして管理しているため、動かしたいアプリケーションがその時間に動いていない場合でもWindowsOSがアプリケーションを起動や実行をしてくれます。

しかしApplication.OnTimeメソッドは、書いていれば勝手に実行されるわけではなく、OnTimeメソッドが書いてある関数、厳密にはOnTimeメソッドの行を実行しない限り実行予約はされません。実行予約はExcelが管理します。言い換えれば、Excelが起動していないと実行できないことになるため、必然的にExcelが起動していることがOnTimeメソッドで指定したプロシージャを動かすための前提になります。

そのため、例えば1時間後にSubTestという関数を動かすようにOnTimeメソッドで実行予約をしたあとにExcelを終了すると実行予約は無かったことになります。その状態で1時間後になる直前の59分後に再度Excelを起動していたとしても実行予約はもう無いので1時間後になっても予約した関数は実行されません。

テレビのレコーダーを例にすると、録画予約をしたけど録画時刻になる前にレコーダの電源プラグをコンセントから抜いてしまった(Excel自体を終了した)ため録画できなかった、という状況が分かりやすいと思います。


OnTimeメソッドは非同期で動作する

OnTimeメソッドは呼び出されると指定したプロシージャの実行予約をするだけで、すぐに次の行の処理を行います。OnTimeメソッド自体が何か処理をするわけではないため指定したプロシージャの実行とは同期しません。

ただし、OnTimeメソッドを呼び出したあとに、呼び出した側の処理が指定時刻になっても終わっていないのであれば、実行予約したメソッドはLatestTime引数の状態に従って実行中のマクロが終わるのを待ちます。

実行予約したマクロのブックを閉じても指定時刻でブックが開く

OnTimeメソッドで実行予約されたプロシージャ(関数)が書かれているブックを閉じても、指定時刻になれば自動で開かれてプロシージャが実行されます。

これは、実行予約がマクロが書いてあるブックではなくExcelで管理されているためです。Excelが起動していれば実行予約は有効のため、指定時刻になったときにブックが閉じられていれば開いてからプロシージャを実行します。しかし、先にも書きましたがExcel自体を終了してしまうと実行予約は無かったことになります。

なお、Excelアプリケーションを複数起動している場合は、それぞれのExcelで実行予約は別管理になります。例えば3つExcelを起動してる場合に、1つ目のExcelでOnTimeメソッドを使って実行予約をしても、それは1つ目のExcelでしか実行されません。2つ目と3つ目のExcelでは実行予約自体がないため実行されません。

別のマクロが動作中に実行予約はどうなるか

別のマクロが動作中にOnTimeメソッドで指定した時刻に達した場合、先に動いていたマクロが終わるのを待ってからOnTimeメソッドで指定した処理が実行されます。

VBA以外の他のプログラミング言語の場合は大抵スレッドの仕組みがあり複数の処理が並列で動作できますが、Excel VBAは並列処理は出来ませんので、OnTimeメソッドを使っても並列処理は残念ながら実現できません。

先に動いていたマクロが終わるのを必ず待って実行したい場合は、OnTimeメソッドのLatestTime引数を省略します。ある程度待って待ちきれない場合は実行しないようにする場合はLatestTime引数にEarliestTime引数に指定した時刻からどれだけ待つのかの時間を指定します。

例えばEarliestTime引数に「TimeValue(“12:00:00”)」と書いて12時に実行するようにしたが10秒待っても実行されない場合で実行しないようにしたい場合はLatestTime引数に「TimeValue(“12:00:10”)」か「TimeValue(“12:00:00”) + TimeValue(“00:00:10”)」のように10秒後が分かるように指定します。


同時刻の実行予約はあとで予約した方が先に実行される

同時刻に複数の実行予約をした場合、実行予約をした順ではなく、最後に実行予約をした方から実行されていきます。

例えば、以下のように3秒後に5つの関数の実行予約をした場合、実行予約はSub1、2、3、4、5の順で行っていますが、実際に実行されるのは逆順のSub5、4、3、2、1の順に行われます。

実行結果
Sub5
Sub4
Sub3
Sub2
Sub1

これはプロセッサのスタック方式としてよく使われるLIFO(後入れ先出し)と呼ばれる方式と同じで、最初に登録したものが一番最後まで残ります。これが嫌な場合は呼び出し順を調整するか、実行予約の時刻をずらすかで対応する必要があります。


サンプルコード

OnTimeメソッドには時刻の指定方法が2通りあります。「何時から」の方法と「今(または、ある時刻)から何時何分何秒後」の方法です。

また、呼び出すプロシージャに引数がある場合には書き方に癖があります。引数の型が文字列型か数値型によって書き方が異なります。引数が1つか2つ以上かでも書き方が異なります。

以下のサンプルコードで詳細を記載しています。なお、OnTimeメソッドで呼び出しているプロシージャ名に「OnTime1」とか「OnTime2」とかを付けていますが、OnTimeメソッド名とは全く関係ありません。実行するプロシージャ名であれば何でも構いません。「CupRamenTimer」とかでもOKです。

指定時刻に実行予約

指定した時刻に実行するように予約する場合の書き方です。

実行結果
2021/10/09 10:37:00

今から一定時間後の実行予約

ある時刻から一定時間後に実行するように予約する場合の書き方です。ここでは「今から3分後」を例としています。

実行結果
2021/10/09 10:37:49
2021/10/09 10:40:49

文字列引数を持つプロシージャの呼び出す実行予約

プロシージャに文字列引数がある場合に、その引数を渡す場合はProcedure引数に「’OnTime2 “abc”‘」のように、全体をシングルクォーテーションで囲み、プロシージャ名の後ろに半角スペースを入れて、文字列引数をダブルクォーテーションで囲んで指定します。引数が複数ある場合は引数間を「”, “」カンマで区切り、「’OnTime3 “abc”, “DEFG”‘」のような形になるように指定します。

以下のコードでは分かりやすいように文字列のダブルクォーテーション部分を & “””” & で明示しています。

実行結果例
2021/10/09 10:42:21
2021/10/09 10:42:21
OnTime3 2021/10/09 10:42:24 abc DEFG
OnTime2 2021/10/09 10:42:24 abc

※このコードは同時刻に実行予約しているため、上記の説明の通り、実行予約順と出力順が逆転しています。

数値型引数を持つプロシージャの呼び出す実行予約

呼び出すプロシージャに数値型引数がある場合は、「’OnTime4 123’」のように、全体をシングルクォーテーションで囲み、プロシージャ名の後ろに半角スペースを入れて数値型引数に渡す数値を指定します。引数が複数ある場合は引数間を「”, “」カンマで区切り、「’OnTime3 123, 4567’」のような形になるように指定します。

実行結果
2021/10/09 10:55:21 123 45678901

文字列型と数値型の引数を持つプロシージャの呼び出す実行予約

文字列型引数と数値型引数がある場合は「’OnTime5 “abc”, 123’」のように指定します。記述方法は上記サンプルコードの通りです。

実行結果
2021/10/09 10:55:49 abc 123