【入門編】【実務中級】Slide.Shapes.Countが膨大なスライドの高速処理:Dictionaryオブジェクトを併用し、特定のオブジェクト名(例:「図 1」)をミリ秒単位でシーク・操作する高速化アプローチ – PowerPoint VBA解析バイブル

スポンサーリンク

こんにちは!PowerPointの自動化、日々の業務でお疲れ様です。
「マクロの記録」で生成されたコードを少し改変して動かしてみたものの、シェイプ(図形や画像)が何百個もある重いスライドを処理させたとたん、画面がフリーズしたかのように固まってしまい、コーヒーを飲み終わっても終わらない……そんな絶望を味わったことはありませんか?

今回は、そんな「重たいPowerPoint資料」を一瞬で、それこそミリ秒単位で料理するための極限のテクニックをお伝えします。ここをクリアすれば、あなたのVBAスキルは「初学者」の領域を完全に抜け出し、現場で頼られる自動化エンジニアの扉を開くことになりますよ。

一緒に本質を学んでいきましょう!

1. なぜ「重いスライド」の処理は遅いのか?(オブジェクトモデルの罠)

PowerPoint VBAで最もよくやる間違い、それは以下のようなコードです。

‘ 【やってはいけないアンチパターン】
Dim shp As Shape
For Each shp In ActivePresentation.Slides(1).Shapes
If shp.Name = “図 1” Then
shp.Top = 100 ‘ 見つかったら移動
End If
Next shp

一見、何の問題もないように見えますよね。「上から順にシェイプをペラペラとめくって、名前が『図 1』だったら動かす」という直感的なコードです。

しかし、ここにPowerPoint VBAのパフォーマンスを殺す最大の罠があります。

COM境界を跨ぐコストの重み

VBA(ExcelやPowerPoint)の世界と、実際のアプリケーション本体(OfficeのC++製エンジン)の間には、「COM(Component Object Model)境界」という見えない壁があります。
`For Each` でシェイプを1つ取得するたびに、この壁を何度も往復しています。シェイプが500個あれば、VBAとPowerPoint本体が500回も会話をしているわけです。これが、マクロが遅くなる本当の原因です。

2. 解決策:Dictionaryで「インデックス(索引)」を作る

大量のデータから特定の項目を爆速で探すとき、現実世界ではどうしますか?
分厚い辞書(Dictionary)の最初から1ページずつめくる人はいませんよね。巻末の「索引(目次)」からページ番号をパッと引くはずです。

VBAでも全く同じアプローチをとります。
スライドを開いた最初に、「シェイプの名前」をキー(Key)、「シェイプのオブジェクトそのもの」をアイテム(Item)として、メモリ上の辞書(Scripting.Dictionary)に一括で放り込むのです。

あとは、探したい名前を指定するだけで、PowerPoint本体に何度も聞きに行くことなく、一瞬で目的のオブジェクトを取り出せるようになります。

3. 【実践】ミリ秒シークを実現する高速化コード

それでは、実際に現場でそのまま使える実用コードを見てみましょう。
あらかじめVBEの「ツール」>「参照設定」から 「Microsoft Scripting Runtime」 にチェックを入れておくか、あるいは遅延バインディング(CreateObject)を使用します。今回は環境を選ばない遅延バインディング版でお見せします。

Option Explicit

Sub HighSpeedShapeSearchSample()
Dim targetSlide As Slide
Dim shp As Shape
Dim dictShapes As Object
Dim targetName As String
Dim startTime As Double

‘ 処理開始時間の計測(パフォーマンス測定用)
startTime = Timer

‘ 対象スライドの取得(ここではアクティブスライド)
Set targetSlide = ActivePresentation.Slides(1)

‘ 1. Dictionaryオブジェクトの生成
Set dictShapes = CreateObject(“Scripting.Dictionary”)

‘ 2. 【肝】スライド内の全シェイプを一度だけスキャンしてキャッシュに登録
‘ ここでCOM境界を跨ぐのは「最初の1回」だけです!
For Each shp In targetSlide.Shapes
‘ キーが重複する可能性を考慮し、存在しない場合のみ追加
If Not dictShapes.Exists(shp.Name) Then
Set dictShapes(shp.Name) = shp
End If
Next shp

Debug.Print “キャッシュ構築完了: ” & (Timer – startTime) 1000 & ” ms”

‘ ==========================================================
‘ 3. ここからが本番:何百個あってもミリ秒で特定オブジェクトを操作
‘ ==========================================================
targetName = “図 1” ‘ 探したいオブジェクト名

If dictShapes.Exists(targetName) Then
‘ Dictionaryから直接オブジェクトを取り出す(PowerPointに再問い合わせしない!)
Dim foundShape As Shape
Set foundShape = dictShapes(targetName)

‘ 好きに料理する
foundShape.Left = 50
foundShape.Top = 50
foundShape.Line.ForeColor.RGB = RGB(255, 0, 0) ‘ 赤枠にする

MsgBox “「” & targetName & “」をミリ秒単位で捕捉し、移動しました!”, vbInformation
Else
MsgBox “指定されたシェイプは見つかりませんでした。”, vbExclamation
End

‘ 4. メモリの解放
Set dictShapes = Nothing

Debug.Print “トータル処理時間: ” & (Timer – startTime) 1000 & ” ms”
End Sub

4. このコードがもたらす圧倒的なメリット

① 2回目以降の検索が「一瞬」になる

もしこのマクロの中で、「図 1」だけでなく「テキストボックス 3」「画像 5」といった複数の特定オブジェクトを連続で操作したい場合、通常の `For Each` だと操作するたびに全シェイプを総当たりするため、O(N^2) の絶望的な遅さになります。
しかし、Dictionaryを使えば検索コストは O(1)(一瞬) です。オブジェクトが何千個あっても速度は落ちません。

② エラーハンドリングに強い

存在しないシェイプ名を指定したとき、通常のコードでは「そんな名前のオブジェクトはありません」という実行時エラー(エラー438やエラー1004など)でマクロが強制終了してしまいます。
しかし、`dictShapes.Exists(targetName)` で事前に安全確認ができるため、堅牢な(バグりにくい)システムが構築できます。

5. 陥りがちな罠と注意点

ここだけは気をつけてほしいポイントをいくつか。

  • シェイプ名の重複に注意

PowerPointでは、手動で図形をコピー&ペーストすると、意図せず「図 1」「図 1」といった重複した名前が生まれることがあります。上記のコードでは `If Not dictShapes.Exists(shp.Name)` で最初の1つ目を優先するようにしていますが、厳密にユニークな名前を扱いたい場合は命名規則を整えておく必要があります。

  • 画面描画の抑制(ScreenUpdating)の併用

もしシェイプの位置やサイズを変更する処理を大量に行う場合は、あらかじめ `ActiveWindow.View.GotoSlide targetSlide.SlideIndex` などの描画を伴う処理を挟まないようにし、必要に応じてバックグラウンドで処理を完結させるとさらに高速化します。

まとめ

いかがでしたでしょうか?
「画面が固まる重いマクロ」を「一瞬で終わるスマートな自動化」に変える鍵は、「何度もオブジェクトを探しに行かない(メモリに覚え込ませる)」というエンジニアリングの基本原則にありました。

このDictionary併用アプローチをマスターすれば、PowerPoint VBAのパフォーマンスに関する悩みはほとんど解決したも同然です。ぜひ、あなたの実務の自動化ツールに応用してみてください。

「ここをもっと深掘りしたい」「こんな重い処理はどう書けばいい?」といった疑問があれば、いつでも先輩エンジニアに聞いてくださいね。それでは、快適なVBAライフを!

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