【テクニカル・上級編】【マルチウィンドウ制御】”Presentation.Windows”コレクションを操作し、複数開いたプレゼンテーションを並列整列・比較編集するテクニック – PowerPoint VBA解析バイブル

スポンサーリンク

【PowerPoint VBAを掌握する極限の知見】マルチウィンドウ制御の深層:`Presentation.Windows`コレクションを支配し、並列比較編集を極める

多くの開発者は、PowerPoint VBAを「単一のプレゼンテーションを上から順に舐める自動化ツール」程度に捉えている。しかし、シニアエンジニアやエンタープライズのアーキテクトが直面する現場では、膨大な資料の突合、複数部門から集約されたスライドの整合性検証など、「複数ウィンドウの同時制御とクロス・プレゼンテーション同期」という過酷な要求が日常茶飯事だ。

本稿では、PowerPointのオブジェクトモデルにおける隠された歯車である `Presentation.Windows` コレクションを完全に手懐け、Windows APIの協調動作すら内包した、実戦投入可能なマルチウィンドウ制御の極意を授ける。

1. PowerPointオブジェクトモデルにおけるウィンドウ階層の真実

まず、Office VBAにおける「Window」概念の歪みを理解しなければならない。ExcelやWordとは異なり、PowerPointは「一つのプロセス(Application)の配下に、複数のPresentation(文書)が存在し、さらに一つのPresentationが複数のWindow(ビュー)を持つ」という多重構造をとっている。

[Application]
┗ [Presentations (Collection)]
┣ [Presentation A] ── ┗ [Windows (Collection)] (Window A1, Window A2…)
┗ [Presentation B] ── ┗ [Windows (Collection)] (Window B1…)

ここで重要なのは、`ActiveWindow` や `ActivePresentation` という曖昧なグローバル状態に依存したコードを書くこと自体が、マルチウィンドウ制御における最大のアンチパターンのひとつであるという点だ。

複数のプレゼンテーションを並行して比較・編集する自動化コードを書く場合、アプリケーションのフォーカスに頼るのではなく、明示的な `DocumentWindow` オブジェクトの参照を保持し、それを操作するアプローチを徹底する。

2. 実装:複数プレゼンテーションのタイル状並列整列とビュー同期

以下のコードは、現在開いているすべてのプレゼンテーションウィンドウをデスクトップ上でタイル状に整列させ、さらに特定のマスター資料と子資料の間でビュー(表示モードや選択スライド)を同期制御するエンジニアリング・サンプルである。

Option Explicit

‘ Windows API: ウィンドウの位置とサイズを強制変更するための宣言
If VBA7 Then
Declare PtrSafe Function MoveWindow Lib “user32” (ByVal hwnd As LongPtr, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal bRepaint As Long) As Long
Else
Declare Function MoveWindow Lib “user32″ (ByVal hwnd As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal bRepaint As Long) As Long
End If

”’

”’ 開いているすべてのプレゼンテーションウィンドウを画面上にタイル状に整列させる
”’

Sub TileAllPresentationWindows()
Dim targetApp As PowerPoint.Application
Set targetApp = ActiveApplication ‘ 冗長性を排除した明示的参照

Dim presCount As Long
presCount = targetApp.Presentations.Count

If presCount = 0 Then
MsgBox “制御対象のプレゼンテーションが開かれていません。”, vbCritical, “致命的エラー”
Exit Sub
End If

‘ 画面解像度の取得(簡易的にメインモニターの想定サイズを使用)
Dim screenWidth As Long: screenWidth = 1920
Dim screenHeight As Long: screenHeight = 1080

Dim cols As Long, rows As Long
‘ ウィンドウのグリッド数を動的計算(正方形に近づける)
cols = WorksheetFunction.RoundUp(Sqr(presCount), 0)
rows = WorksheetFunction.RoundUp(presCount / cols, 0)

Dim cellWidth As Long: cellWidth = screenWidth \ cols
Dim cellHeight As Long: cellHeight = screenHeight \ rows

Dim i As Long, j As Long, k As Long
k = 0

Dim pres As PowerPoint.Presentation
Dim win As PowerPoint.DocumentWindow

‘ オブジェクトのライフサイクルを意識し、メモリリークを防ぐ走査
For i = 1 To targetApp.Presentations.Count
Set pres = targetApp.Presentations(i)

‘ 1つのプレゼンテーションに対して複数のウィンドウが存在し得る
For j = 1 To pres.Windows.Count
Set win = pres.Windows(j)

‘ ウィンドウをアクティブにしてからAPIで物理制御
win.Activate

Dim gridX As Long, gridY As Long
gridX = (k Mod cols) cellWidth
gridY = (k \ cols) cellHeight

‘ Win32 API を利用した確実なウィンドウ配置
#If VBA7 Then
Dim winHandle As LongPtr
winHandle = win.HWND
#Else
Dim winHandle As Long
winHandle = win.HWND
#End If

Call MoveWindow(winHandle, gridX, gridY, cellWidth, cellHeight, 1)

k = k + 1

‘ オブジェクト変数の即座の解放
Set win = Nothing
Next j
Set pres = Nothing
Next i

MsgBox “ウィンドウのタイル状整列が完了しました。”, vbInformation, “完了”
End Sub

3. レガシー環境とメモリ最適化の極意

複数ウィンドウを操作するマクロを実行する際、PowerPointの画面描画エンジン(GDI+)は強烈な負荷を受ける。特に、大量の図形や高解像度画像が含まれるプレゼンテーションを複数同時にアクティブ化すると、画面のちらつき(フリッカ)だけでなく、スタックオーバーフローやCOM例外(Error 0x8001010A: サーバーが忙しいため、操作を完了できません)が容赦なく発生する。

これを防ぐための、プロフェッショナルだけが知る鉄則を挙げる。

1. 画面描画の完全な抑制 (`ScreenUpdating` の罠)

Excelには `Application.ScreenUpdating` が存在するが、PowerPointのApplicationオブジェクトにはこのプロパティが存在しない。そのため、ウィンドウの切り替え時に発生する描画コストを抑えるには、各 `DocumentWindow` の `ViewType` を一時的にスライドショーや標準ビューから切り替えるか、Windows APIの `SendMessage` でウィンドウの再描画を一時停止する高度なテクニックが必要となる。

2. オブジェクトの明示的破棄(メモリリークの根絶)

VBAのガベージコレクションは遅延実行される。ループ内で `pres.Windows(j)` のようなプロパティアクセスを繰り返すと、COMラッパーオブジェクトがメモリ上に蓄積され、長時間のバッチ処理で確実にメモリリークを起こす。
必ずループの各イテレーションの終わりに、以下のように明示的なオブジェクトの破棄を行え。

Set win = Nothing
Set pres = Nothing

4. システム間連携における実戦知見:外部データとの比較・同期

例えば、「基幹システムから出力された最新のExcel/CSVデータ」と「PowerPoint上の表スライド」を左右に並べて視覚的・動的に比較・修正させる仕組みを構築する場合、`Presentation.Windows` を用いたウィンドウのドッキング制御が真価を発揮する。

”’

”’ 2つの特定のプレゼンテーションを左右に強制配置し、スライドインデックスを同期させる
”’

Sub SyncTwoPresentationsSideBySide(ByVal pathA As String, ByVal pathB As String)
Dim app As PowerPoint.Application
Set app = Application

Dim presA As PowerPoint.Presentation, presB As PowerPoint.Presentation
Set presA = app.Presentations.Open(pathA, ReadOnly:=msoTrue)
Set presB = app.Presentations.Open(pathB, ReadOnly:=msoTrue)

Dim winA As PowerPoint.DocumentWindow, winB As PowerPoint.DocumentWindow
Set winA = presA.Windows(1)
Set winB = presB.Windows(1)

‘ 左右分割配置の計算 (FHD前提)
#If VBA7 Then
Dim hwndA As LongPtr, hwndB As LongPtr
#Else
Dim hwndA As Long, hwndB As Long
#End If

hwndA = winA.HWND
hwndB = winB.HWND

‘ 左半分にウィンドウA、右半分にウィンドウBを配置
Call MoveWindow(hwndA, 0, 0, 960, 1080, 1)
Call MoveWindow(hwndB, 960, 0, 960, 1080, 1)

‘ ビューの同期ループ(例:ウィンドウAのスライド切り替えをウィンドウBに追従させる)
‘ ※実運用ではイベントハンドラやポーリング機構をここに組み込む

‘ 解放
Set winA = Nothing: Set winB = Nothing
Set presA = Nothing: Set presB = Nothing
End Sub

総括

PowerPoint VBAにおけるマルチウィンドウ制御は、単なる「見た目の操作」ではない。背後にあるCOMアーキテクチャの制約、Win32 APIとの境界領域、そしてメモリ管理の厳格さを理解した者だけが扱える、極めて強力な武器である。

「動けばいい」というアマチュアのコードを捨て去り、ウィンドウのライフサイクルとハードウェアリソースを完全に掌握した堅牢なシステムを構築せよ。それこそが、真のエンジニアリングである。

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