【入門編】【上級者向け】大量メール送信時のOutlookフリーズを防ぐための「DoEvents」と「待機処理」の最適化 – Outlook VBA解析バイブル

スポンサーリンク

【Outlook VBA】大量メール送信で二度とフリーズさせない!「DoEvents」と「待機処理」の極限最適化ガイド

こんにちは!日々の業務自動化、本当にお疲れ様です。

VBAを少しずつマスターして、「よし、次は数百件、数千件のメール自動送信に挑戦だ!」と意気込んで実行したボタン。その直後、画面が真っ白になり、タイトルバーには恐怖の「(応答なし)」の文字……。誰もが一度は通る、冷や汗が流れる瞬間ですよね。

「PCのスペックが低いのかな?」と思われがちですが、実はこれ、OutlookとWindowsの『会話のキャッチボール』が途切れてしまったことが原因なのです。

この記事では、マクロの記録を卒業し、一歩先の上級プログラマーへステップアップするための「画面をフリーズさせずに、大量メールをバックグラウンドで安全に高速処理する極意」を、優しく、かつ実践的に解説します。

ここをクリアすれば、あなたのOutlook VBAは「ただ動くマクロ」から「誰もが安心して使えるプロ仕様のシステム」へと進化しますよ!

—

1. なぜ大量送信時にOutlookは「応答なし」になるのか?

原因を理解するために、まずはOutlook君の「脳内」をのぞいてみましょう。

Outlook(そしてVBA)は、基本的に「シングルスレッド」という仕組みで動いています。これは、「一度に1つのことしか処理できない、超真面目で不器用な職人さん」のようなものです。

【PCの画面が固まるメカニズム】

[OS(Windows)] ──「最近どう? 動いてる?」──> [Outlook君(VBA実行中)]
▲ │
│ (返事がない…フリーズしたな?) │ (メール作成に超集中!)
│ ▼
[画面が真っ白に!] <─────────── 反応なし ─────────────── [応答を無視] VBAがループ処理で1,000件のメールを連続作成しているとき、Outlook君は「メールを作る」という作業に100%の脳内リソースを割いています。 この間、Windows OSから「ねえ、画面を更新して」「マウスがクリックされたよ」といったメッセージ(イベント)が送られてきているのですが、Outlook君はそれを完全に無視してしまいます。 返事が一定時間途絶えるため、Windowsは「このアプリはフリーズした」と判断し、画面を白く濁らせてしまうのです。

—

2. 救世主「DoEvents」の真実と、よくある「罠」

この「職人立ち往生」を防ぐための魔法のコマンドが `DoEvents` です。

`DoEvents` とは、一言で言えば「Outlook君に、一瞬だけ作業の手を止めさせて、Windowsからのメッセージに返事をさせる(お茶を濁してもらう)命令」です。

「じゃあ、ループの中に毎回 `DoEvents` を入れればいいんだね!」

そう思って、以下のようなコードを書くと、実は別の罠にハマってしまいます。

‘ [悪い例]毎回 DoEvents を呼んでしまう
For i = 1 To 1000
CreateMailAndSend(i)
DoEvents ‘ 毎回お茶を引いていては、逆にめちゃくちゃ遅くなる!
Next i

なぜ毎回 `DoEvents` を呼ぶとダメなのか?

`DoEvents` は非常に強力ですが、実行するたびに「OSとOutlookの間で膨大なやり取り」が発生するため、処理速度が劇的に低下(オーバーヘッド)します。
1,000回メールを送るのに、毎回お茶休憩を挟んでいたら、日が暮れてしまいますよね。

解決策:モジュロ演算(Mod)で「50通に1回」息抜きをさせる

プロが実践するアプローチは、「一定の件数ごと(例:50件ごと)にだけ `DoEvents` を実行する」というバッチ制御です。

‘ [プロの常套手段]50件に1回だけ息抜きをさせる
If i Mod 50 = 0 Then
DoEvents
End If

—

3. Outlookには「Application.Wait」がない!? Windows APIによる精密な待機処理

Excel VBAでおなじみの `Application.Wait`。実はこれ、Outlook VBAには存在しません。
Excelの感覚で使おうとすると、「オブジェクトはこのプロパティまたはメソッドをサポートしていません」と冷たく怒られてしまいます。

そこで、大量送信時にメールサーバーやOutlookのバッファがパンクしないよう、Windowsの心臓部の機能(API)を直接借りて「ミリ秒単位の精密な待機(Sleep)」を実現します。

32bit / 64bit 両対応の「Sleep」宣言

お使いのOfficeが32bit版でも64bit版でも安全に動作するように、モジュールの最上部(General Declarations)に以下のコードを記述します。

If VBA7 Then
‘ 64bit Office用の宣言
Private Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
Else
‘ 32bit Office用の宣言
Private Declare Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
End If

これを記述しておくことで、コード内で `Sleep 100`(0.1秒待機)や `Sleep 1000`(1秒待機)という超精密な待機処理が使えるようになります。

—

4. 隠れた最重要ポイント:COMオブジェクトの「明示的解放」

大量送信時のフリーズや強制終了のもう一つの大きな原因は、メモリのパンク(枯渇)です。

VBAで `CreateItem(olMailItem)` を使ってメールを作るたび、パソコンのメモリ上に「メールオブジェクト」が生成されます。
ループの中でこれを適切に片付け(解放)しないと、裏で目に見えないゴミが溜まり続け、やがてOutlookがクラッシュします。

ループの最後で必ず、作成したメールオブジェクトを `Nothing` にして、メモリを綺麗に掃除しましょう。

Set objMail = Nothing ‘ これが、メモリを綺麗に保つ「魔法の片付け」

—

5. 【完全版】フリーズを完璧に防ぐ、堅牢な大量送信テンプレートコード

それでは、これまでの知見をすべて融合した、そのまま実務にコピペして使える「極限最適化された大量送信マクロ」の全コードを紹介します。

このコードは、「50件ごとに画面を更新(DoEvents)し、10件ごとに0.5秒のインターバル(Sleep)を挟み、メモリも都度解放する」という、プロが書く設計そのものです。

Option Explicit

‘ — Windows API の宣言(32bit / 64bit 両対応) —
If VBA7 Then
Private Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
Else
Private Declare Sub Sleep Lib “kernel32″ (ByVal dwMilliseconds As Long)
End If

”’

”’ 安全かつ高速に大量のメールを送信するメインルーチン
”’

Sub SafeBulkMailSender()
Dim olApp As Outlook.Application
Dim olMail As Outlook.MailItem
Dim i As Long
Dim totalMails As Long

‘ テスト用の送信件数(実務に合わせて変更してください)
totalMails = 200

‘ Outlookアプリケーションオブジェクトの取得
Set olApp = New Outlook.Application

‘ 画面描画の一時停止や、警告の抑止はOutlookではできないため、
‘ 設計(ロジック)で負荷をコントロールします。

On Error GoTo ErrorHandler

Debug.Print “— 送信処理を開始します —”

For i = 1 To totalMails

‘ 1. メールオブジェクトの作成
Set olMail = olApp.CreateItem(olMailItem)

‘ 2. メールの内容設定
With olMail
.To = “dummy_recipient_” & i & “@example.com” ‘ 宛先
.Subject = “【重要なお知らせ】定期配信メール(No. ” & i & “)”
.Body = “本メールはテスト自動送信です。” & vbCrLf & _
“処理件数: ” & i & ” / ” & totalMails & ” 件目”

‘ ※ テスト時は「.Display」や「.Send」の代わりに「.Save」にすると
‘ 下書きフォルダに保存されるため、安全にテストが可能です。
.Save
‘.Send ‘ 本番時はこちらを有効化
End With

‘ 3. 【最重要】オブジェクトの即時解放(メモリリーク防止)
‘ ループ内で使い終わったメールオブジェクトは、即座にNothingにします。
Set olMail = Nothing

‘ 4. インターバル制御(10通ごとに500ミリ秒 = 0.5秒 待機)
‘ メールサーバーに「スパム(迷惑メール)」と判定されるのを防ぐ効果もあります。
If i Mod 10 = 0 Then
Sleep 500
End If

‘ 5. UI(画面)の応答性確保(50通ごとにOSへ制御を戻す)
‘ これにより、画面が「応答なし」になるのを防ぎ、進捗状況が更新されます。
If i Mod 50 = 0 Then
DoEvents
Debug.Print i & ” 件目の処理が完了。システムをリフレッシュしました。”
End If

Next i

‘ 完了メッセージ
MsgBox totalMails & ” 件のメール処理が安全に完了しました!”, vbInformation, “処理完了”

ExitSub:
‘ 後処理:オブジェクトの参照を完全に破棄
Set olMail = Nothing
Set olApp = Nothing
Exit Sub

ErrorHandler:
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical, “エラー”
Resume ExitSub
End Sub

—

6. コードの解説と「プロのこだわりポイント」

このコードが、なぜ「フリーズしない」のか、そのポイントを整理しておきましょう。

1. `i Mod 10 = 0` で `Sleep 500` を挟む理由
一気に数百件のメールをメールサーバー(SMTPサーバー)に投げつけると、サーバー側が「攻撃を受けている!」と誤認して接続を遮断することがあります。あえて「少しだけ立ち止まる(0.5秒)」ことで、サーバーにも優しい上品な動きになります。
2. `i Mod 50 = 0` で `DoEvents` を呼ぶ理由
画面がフリーズするのを防ぐ「防波堤」の役割です。50通に1回、Windowsに「生きてるよ!」と伝えるだけで、画面の「応答なし」は完全に回避できます。
3. `Set olMail = Nothing` の位置
`Next i` の直前に置くことで、ループが次の周回に入る前に、いま使ったメモリを完全に空っぽにしています。これにより、何千件処理してもOutlookのメモリ使用量は一定に保たれます。

—

まとめ:ここをクリアすれば、Outlook VBAの基本はバッチリです!

今回は、少し高度な「Windows API」や「メモリ管理」といった、一歩進んだテーマをお届けしました。

最初は難しく感じられたかもしれませんが、やっていることは非常にシンプルです。

  • OSに定期的に「生きてるよ」と伝える(DoEventsの最適化)
  • サーバーがパンクしないよう、少しだけ休憩を入れる(Sleep)
  • 使ったおもちゃは、すぐに箱に片付ける(Set Nothing)

この3つの原則を守るだけで、あなたの書くVBAコードは、劇的にプロっぽく、そして何より「壊れない頑丈なシステム」へと生まれ変わります。

業務効率化の大きな一歩を応援しています。もし職場で「お、このマクロ全然固まらないね!」と褒められたら、ぜひ心の中でガッツポーズをしてくださいね。

また次のステップでお会いしましょう!応援しています!

タイトルとURLをコピーしました