VBA でWaitやSleepを使う際の問題

VBAには、そのものずばりの Wait もしくは Sleep がありません。


Application.Wait

Application.Wait は指定した「時間」の停止ではなく、指定した「時刻」まで処理を停止するというよく分からない仕様です。そして何より、Application.Waitは動作が安定していません。

試しに次のようなプロシージャーを実行してみます。

Sub uTest()
    Dim uStart
    
    uStart = Timer
    uWait 1
    Debug.Print Timer - uStart '経過時間を表示
End Sub

Sub uWait(ByVal uSeconds)
    Application.Wait (Now + uSeconds / 86400)
End Sub

結果は次の通りで、全く安定しません。

 0.921875 
 0.78125 
 0.90625 
 1 
 0.234375 
 0.4453125 
 0.8515625 
 0.03125 
 0.59375 

私の使い方が悪いのかバグがあるのか。


Timer 関数

そこで、Timer関数を使って次のようなプロシージャーを書いて見ました。

参考にしたのは、次のページです。


Sub uWait(ByVal uSeconds As Single)
    Dim uStart As Single
    Dim uElapsed As Single
    
    uStart = Timer
    
    Do
        uElapsed = Timer - uStart
        If uElapsed < 0 Then '午前0時を超えた場合
            uElapsed = uElapsed + 86400 '60 * 60 * 24 seconds
        End If
    Loop Until uElapsed > uSeconds
End Sub

4回実行した結果は、次のとおり概ね正確です。

 1.001953 
 1.001953 
 1.001953 
 1.001953 

こちらを使った方が良さそうです。


Timer 関数は午前0時からの経過時間を返す関数なので、午前0時を超えてしまうと時間がリセットされ0に戻ってしまいます。なので、午前0時を超えた時の処理を入れてあるけれど、動作は未確認です。


Win 32 API の Sleep

次のように、Win32 APIのSleepが紹介されているサイトも多いです。

Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal ms As LongPtr)

これを試したところ、上記のTimerによるプロシージャーと同様に正確でした。

Sleepの方が宣言を書くだけなので簡単ですが、どうも Declare で外部プロシージャーを呼び出すのは好きになれません。


そして、Microsoft 365 Defender for Business では、次のルールを有効化することが推奨されています。

Block Win32 API calls from Office macros

Attack surface reduction rules reference - Microsoft Defender for Endpoint | Microsoft Learn

これを有効にしたら、Win 32 APIを使っているマクロは動かなくなってしまうのではないかと思います。

将来を考えると、Win 32 APIを使うのは避けておいた方が良さそうな気がします。


コメント

アクセス数の多い投稿

セキュリティ対策ソフトのノートンが詐欺ソフトまがいになってしまってショック

ZIPファイルを開こうとすると、展開を完了できません、と言われる

突然滅茶苦茶遅くなったPCがWindows Updateのキャッシュクリアで復活

Windows セキュリティーのビックリマークが消えない

Power Automate Desktopでブラウザでダウンロードしたファイルを処理する

Excel 2019 クエリが原因で日本語入力の一文字目が勝手に確定する

Excel VBAからODBCを使ってデータを簡単に取得する

NEC Aterm WX5400HP をセットアップ

Teamsで日本語入力すると左上に変換ウィンドウが出る

オカムラ家具のOAチェアー、コンテッサを分解清掃