🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

Excel VBA の Sleep — Windows API 呼び出し、64 ビットの PtrSafe の罠、そして Wait と Sleep

|

Excel VBA の Sleep — Windows API 呼び出し、64 ビットの PtrSafe の罠、そして Wait と Sleep

TL;DRSleep は VBA の一部ではありません。マクロをミリ秒数だけ一時停止させる Windows API 関数で、呼ぶ前に宣言しなければなりません。64 ビット Excel では、その宣言は PtrSafe 属性を帯び、どこでもコンパイルできるよう #If VBA7 のガードで包む必要があります。

#If VBA7 Then
    Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#Else
    Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#End If

Sub PauseQuarterSecond()
    Sleep 250            ' 250 ミリ秒 = 4 分の 1 秒
End Sub

人が Sleep に手を伸ばすのは、Application.Wait では足りないとき — 1 秒まるごとではなく、その端数だけ止めたいときです。そして真っ先に起きるのがコンパイルエラー。Sleep はそもそも VBA のコマンドではなく、Windows に住んでいるからです。本記事はこの 1 つの考えを軸にしています — Sleep は OS から借りてくるミリ秒単位の一時停止だ — それが宣言のこと、64 ビットの罠、そして Sleep でもやはり応答的な停止は得られない理由を説明するからです。

この記事で学べること

  • 考え方の軸 — Sleep は VBA のキーワードではなく、借りてくる Windows の kernel32 関数
  • 64 ビットの PtrSafe の罠 — 正確なコンパイルエラーと、正確な #If VBA7 の直し方
  • 秒ではなくミリ秒 — Sleep を使う理由そのものである 1 秒未満の精度
  • 「ミリ秒」が「正確」でない理由 — 約 15 ミリ秒という OS スケジューラの下限
  • Sleep でもやはりExcel が固まる理由、そして代わりに WaitDoEvents ループを使うべきとき

考え方の軸:Windows から借りてくる一時停止

Application.Wait は Excel 自身の道具です。Sleep は違います — Windows のもので、kernel32 というシステムライブラリの中にあります。使うには、Declare 文で VBA の外へ手を伸ばし、いわばこう言います。「kernel32 の向こうに Sleep という関数がある。これがその形だ。呼ばせてくれ」。そうして初めて Sleep 250 と書けます。

この「Windows から借りる」が考え方の軸で、Sleep の厄介さはすべてここから来ます。VBA ネイティブのキーワードなら、ただ動きます。借りもの API 関数は、宣言しなければならず、OS の呼び出し規約に一致させねばならず、そして — これが肝心 — 32 ビットと 64 ビットの Windows で違うふうに宣言しなければなりません。その最後の点で、ほぼ全員がつまずきます。

64 ビットの PtrSafe の罠:エラーと直し方

これが Sleep のナンバーワンの問題で、「古いパソコンでは動いていた」マクロが突然動くのを拒む理由です。古いコードや古いフォーラム投稿で見かける定番の宣言はこれです。

Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)   ' 2010 年以前のスタイル

これを現代の64 ビット Excel で走らせると、一行も実行されないうちに VBA は止まり、こう言います。

Compile error: The code in this project must be updated for use on 64-bit systems. Please review and update Declare statements and then mark them with the PtrSafe attribute.

直し方は二段構えです。まず、PtrSafe キーワードを足します。これは宣言が 64 ビットのポインタ安全性について確認済みだと VBA に伝えます。次に、#If VBA7 の条件付きコンパイルブロックで包み、PtrSafe より前の大昔の Excel でも同じファイルがコンパイルできるようにします。

#If VBA7 Then
    ' Excel 2010 以降 - 64 ビット安全
    Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#Else
    ' Excel 2007 以前 - PtrSafe キーワードは存在しない
    Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#End If

#If VBA7 は実行時ではなくコンパイル時に評価されるので、各 Excel バージョンは自分が理解できる一つの宣言だけを見ます。このブロックを標準モジュールの先頭、どのプロシージャよりも上に置けば、Sleep はあらゆる現代の Excel でコンパイルされ動きます。ずっと 64 ビット Excel 365 だけを対象にするなら PtrSafe の行だけで十分ですが、ガード付きのほうが安全にコピペできます。

秒ではなくミリ秒 — Sleep が存在する理由

Sleepミリ秒を取ります。Sleep 250 は 4 分の 1 秒、Sleep 1000 は 1 秒、Sleep 50 は 50 分の 1 秒です。これこそ、秒単位でしか止められない Application.Wait より Sleep を選ぶ理由のすべてです。ループを毎秒数回に間引く、API リクエストの間隔を空ける、何かが落ち着くのを少しだけ待つ — こうしたときに Sleep は適切な分解能を持つ道具です。

Do
    ' ... エクスポートファイルが現れたか確認する ...
    If Dir(exportPath) <> "" Then Exit Do
    Sleep 200        ' ディスクを叩き続けず、毎秒 5 回だけポーリングする
Loop

ただし「ミリ秒」は「正確」ではない

細かい単位を細かい精度と取り違えないでください。Windows はおよそ15.6 ミリ秒のティックでスレッドをスケジュールするので、Sleep 1 は 1 ミリ秒眠るのではなく、次のスケジューラのティックまで — ふつう 15 ミリ秒あたりまで眠ります。Sleep が保証するのは「少なくともこれだけ」であって、決して「きっかりこれだけ」ではなく、実際の停止はスケジューラの粒度へ切り上げられます。「だいたいこのくらいの頻度で」あればよい間引きやペース調整には、それで十分です。正確なタイミングや正確な計測が要るなら、これは間違った道具です — 経過時間を測るには Timer を、高精度のタイミングには QueryPerformanceCounter まで下りることになります。

Sleep もやはり Excel を固める

SleepApplication.Wait と共有する罠がこれです。止まっている間、Excel は固まりますSleep は Excel のたった一本のスレッドをその間ずっと止め置き、メッセージキューを処理しないので、画面は再描画されず、クリックは処理されず、十分に長い Sleep — あるいはループの中の短い Sleep の連発 — はウィンドウを**「応答なし」**にします。1 秒未満の精度でもこれは変わりません。停止は停止であり、ブロックされたスレッドは固まった Excel です。

ですから SleepWait と同じく、停止の目的がユーザーに何かを見せる・させることであるときは間違った道具です。Excel が生きたままの停止は、DoEvents で制御を譲るループです。

' 応答的な 1 秒未満のペース調整 - 拍と拍の間も Excel は生きている
Dim nextBeat As Double
nextBeat = Timer + 0.25
Do While Timer < nextBeat
    DoEvents
Loop

正直な結論:Wait、Sleep、それとも DoEvents ループ

本当に必要なものに沿って、三つを並べてみましょう。

  • 手間なしの整数秒の停止Application.Wait Now + TimeValue(...)DeclarePtrSafe もなく、間違えるところもありません。「2 秒くらい待つ」ならまずここへ。
  • 1 秒未満の停止SleepApplication.Wait は 1 秒より下へ行けないからです。DeclarePtrSafe の儀式は、ミリ秒分解能の代金として受け入れましょう。
  • Excel が応答的であり続けねばならない停止DoEvents ループ。WaitSleep もウィンドウを固めるからです。これは、人が最もよく必要とし、最も手を伸ばさない選択肢です。

あえて挑発しておきます。マクロを遅くするために「念のため」の Sleep を足さないこと。人工的な停止なしにマクロが誤動作するなら、その停止はたいてい本物のバグを隠しています — まだ用意できていない値を読んだ、イベントが二度発火した — そして正直な直し方はそれに対処するのであって、上から振りかけた Sleep 500 ではありません。

ExcelMaster の活用

Sleep の宣言は、まさに間違えやすく、正しく書くのが面倒な種類の定型句です。Lib "kernel32" の文字列、ByVal の引数、PtrSafe 属性、#If VBA7 のガード、そのうえで Sleep が本当に欲しかった道具なのか、それとも Application.WaitDoEvents ループなのか、という判断まで。

ExcelMaster は、正しく 64 ビット安全な宣言をあなたの代わりに書き、そしてより大事なことに、あなたが述べた内容に合った待機の構成を選びます。「200 ミリ秒ごとにファイルをポーリングして」と頼めばガード付きの Sleep ループを、「止めたい、でもキャンセルはさせて」と頼めば代わりに DoEvents ループを返します — だから、64 ビット Excel でクラッシュする 2010 年以前の Declare を出荷することも、生かしておくつもりだった Excel を固めることもありません。

よくある質問

Excel VBA で Sleep を使うには?

標準モジュールの先頭で一度宣言し、ミリ秒値を渡して呼び出します。64 ビット安全な形を使いましょう。#If VBA7 Then Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)#Else 分岐には古い Excel 向けの素の Declare を置く)。そうすれば Sleep 250 がマクロを 250 ミリ秒 — 4 分の 1 秒 — 一時停止させます。

64 ビット Excel で Sleep の Declare がコンパイルエラーになるのはなぜですか?

古い宣言スタイルが 64 ビット Office より前のものだからです。現代の 64 ビット Excel は、すべての Declare 文に PtrSafe 属性を要求し、それがないと "The code in this project must be updated for use on 64-bit systems… mark them with the PtrSafe attribute." が出ます。Declare の後に PtrSafe を足し、その行を #If VBA7 ブロックで包んで、古い Excel でもコンパイルできるようにしましょう。

Sleep と Application.Wait の違いは何ですか?

Sleep は自分で宣言しなければならない Windows API 呼び出しで、ミリ秒数だけ止め、1 秒未満の精度を与えます。Application.Wait は Excel に組み込まれ、宣言が不要で、壁時計の瞬間まで秒単位の分解能で止めます。1 秒より細かい精度が要るときは Sleep を、単純な整数秒の停止には Application.Wait を使いましょう。どちらも待っている間は Excel を固めます。

Sleep は Excel を応答不能にしますか?

はい。Sleep は停止の間ずっと Excel のたった一本のスレッドをブロックし、メッセージを処理しないので、ウィンドウは再描画もクリック処理もできず、「応答なし」と表示されることがあります。停止の間も Excel を応答的に保ちたいなら — 進捗やキャンセルボタンのために — Sleep ではなく DoEvents を呼ぶループを使いましょう。

Sleep はミリ秒単位で正確ですか?

いいえ。Windows は約 15.6 ミリ秒のティックでスレッドをスケジュールするので、Sleep 1 は実際にはおよそ 15 ミリ秒止まり、Sleep は「少なくともこれだけ」しか保証しません。間引きやペース調整には十分ですが、正確なタイミングには向きません。コードにかかる時間を測るには、代わりに Timer 関数を使いましょう。

検証環境

検証環境: Excel 365 (Windows 11, 64 ビット), VBA 7.1 — 最終確認 2026-08-19。

関連ガイド: VBA Wait · VBA Timer · VBA DoEvents · VBA On Error · VBA ScreenUpdating