TL;DR —
Sleepは 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 が固まる理由、そして代わりに Wait や DoEvents ループを使うべきとき
考え方の軸: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 を固める
Sleep が Application.Wait と共有する罠がこれです。止まっている間、Excel は固まります。Sleep は Excel のたった一本のスレッドをその間ずっと止め置き、メッセージキューを処理しないので、画面は再描画されず、クリックは処理されず、十分に長い Sleep — あるいはループの中の短い Sleep の連発 — はウィンドウを**「応答なし」**にします。1 秒未満の精度でもこれは変わりません。停止は停止であり、ブロックされたスレッドは固まった Excel です。
ですから Sleep も Wait と同じく、停止の目的がユーザーに何かを見せる・させることであるときは間違った道具です。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(...)。DeclareもPtrSafeもなく、間違えるところもありません。「2 秒くらい待つ」ならまずここへ。 - 1 秒未満の停止 —
Sleep。Application.Waitは 1 秒より下へ行けないからです。Declare/PtrSafeの儀式は、ミリ秒分解能の代金として受け入れましょう。 - Excel が応答的であり続けねばならない停止 —
DoEventsループ。WaitもSleepもウィンドウを固めるからです。これは、人が最もよく必要とし、最も手を伸ばさない選択肢です。
あえて挑発しておきます。マクロを遅くするために「念のため」の Sleep を足さないこと。人工的な停止なしにマクロが誤動作するなら、その停止はたいてい本物のバグを隠しています — まだ用意できていない値を読んだ、イベントが二度発火した — そして正直な直し方はそれに対処するのであって、上から振りかけた Sleep 500 ではありません。
ExcelMaster の活用
Sleep の宣言は、まさに間違えやすく、正しく書くのが面倒な種類の定型句です。Lib "kernel32" の文字列、ByVal の引数、PtrSafe 属性、#If VBA7 のガード、そのうえで Sleep が本当に欲しかった道具なのか、それとも Application.Wait や DoEvents ループなのか、という判断まで。
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
