【実務・中級編】【プロフェッショナル】PowerPoint上での疑似「OnTime」タイマーの実装:WithEventsとWindows APIを組み合わせた編集中の自動バックアップエンジン – PowerPoint VBA解析バイブル

スポンサーリンク

序論:なぜPowerPoint VBAには「OnTime」がないのか?

Excel VBAを使い慣れたエンジニアがPowerPoint VBAに転向した際、最初に突き当たる壁が「Application.OnTimeが存在しない」という絶望的な事実だ。

Excelにおける`OnTime`は、バックグラウンド処理や定期実行を司る心臓部だが、プレゼンテーションという「静的なスライドの表示」を主目的とするPowerPointにおいて、Microsoftは長らくこの機能を不要と判断してきた。

しかし、実務の最前線ではどうだ?
「編集中のプレゼンを5分おきに自動バックアップしたい」「外部DBの数値をリアルタイムでスライドに反映させたい」といった高度な自動化要求は後を絶たない。

今回は、Windows APIの低レイヤーなタイマー機能をVBAのクラスモジュールと融合させ、「PowerPointが落ちない、極めて堅牢な疑似OnTimeエンジン」を構築する。これは単なるコードの紹介ではない。オブジェクトのライフサイクルを制御し、メモリ管理を掌握するための「設計思想」の伝授である。

1. 設計の要諦:なぜWindows APIとWithEventsの組み合わせなのか?

単にWindows APIの`SetTimer`を呼び出すだけなら、誰でもできる。しかし、それでは不十分だ。VBAにおけるタイマー実装には、以下の2つの「死の罠」が存在する。

1. ゴースト・タイマー現象: プレゼンテーションを閉じてもタイマーがメモリに残り続け、数分後にクラッシュを引き起こす。
2. 実行コンテキストの衝突: スライドの編集(テキスト入力中)やモーダルダイアログの表示中にタイマーが発火すると、VBA実行エンジンが不正終了するリスクがある。

これらを回避するため、我々は「監視用クラスモジュール(WithEvents)」「APIコールバック専用の標準モジュール」を分離し、アプリケーションのイベント(PresentationClose等)と同期させる設計を採る。

2. 実装:自動バックアップ・エンジン

このエンジンは、指定した間隔で現在のファイルを別フォルダに退避させる「オートセーブ」機能を実装する。

2.1. 標準モジュール(modTimerGate)

タイマーの心臓部だ。Windows APIは「標準モジュールの関数ポインタ」しか受け付けない。ここには必要最小限のロジックのみを置く。

Option Explicit

‘ — Windows API 宣言 (64bit/32bit両対応) —
If VBA7 Then
Public Declare PtrSafe Function SetTimer Lib “user32” (ByVal hWnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As LongPtr
Public Declare PtrSafe Function KillTimer Lib “user32” (ByVal hWnd As LongPtr, ByVal nIDEvent As LongPtr) As Long
Else
Public Declare Function SetTimer Lib “user32” (ByVal hWnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Public Declare Function KillTimer Lib “user32” (ByVal hWnd As Long, ByVal nIDEvent As Long) As Long
End If

Public TimerID As LongPtr
Public BackupEngine As clsBackupTimer

‘ タイマーから呼ばれるコールバック関数
Public Sub TimerProc(ByVal hWnd As LongPtr, ByVal uMsg As Long, ByVal idEvent As LongPtr, ByVal dwTime As Long)
On Error Resume Next
‘ クラス側の実行メソッドをキックする
If Not BackupEngine Is Nothing Then
BackupEngine.ExecuteBackup
End If
End Sub

2.2. クラスモジュール(clsBackupTimer)

ここが司令塔だ。PowerPointのイベントを監視し、タイマーのライフサイクルを完全に制御する。

Option Explicit

Private WithEvents App As PowerPoint.Application
Private m_Interval As Long ‘ ミリ秒

‘ 初期化:アプリケーションを紐付け
Private Sub Class_Initialize()
Set App = PowerPoint.Application
End Sub

‘ エンジンの起動
Public Sub StartEngine(ByVal IntervalSeconds As Long)
m_Interval = IntervalSeconds 1000
If TimerID <> 0 Then StopEngine ‘ 二重起動防止

‘ タイマーセット(TimerProcのメモリアドレスを渡す)
TimerID = SetTimer(0, 0, m_Interval, AddressOf TimerProc)
Debug.Print “Backup Engine Started: ” & Now
End Sub

‘ エンジンの停止(これを忘れるとPPTがクラッシュする)
Public Sub StopEngine()
If TimerID <> 0 Then
KillTimer 0, TimerID
TimerID = 0
Debug.Print “Backup Engine Stopped: ” & Now
End If
End Sub

‘ メインロジック:自動保存の実行
Public Sub ExecuteBackup()
Dim TargetPres As Presentation
Set TargetPres = ActivePresentation

‘ 編集モード中や、ファイルが未保存の場合はスキップ(堅牢性の確保)
If TargetPres Is Nothing Or TargetPres.Path = “” Then Exit Sub

‘ ユーザーの作業を邪魔しないよう、バックグラウンドでの保存処理
Dim backupPath As String
backupPath = TargetPres.Path & “\Backup_” & Format(Now, “yyyymmdd_hhnnss”) & “_” & TargetPres.Name

On Error GoTo ErrHandler
TargetPres.SaveCopyAs backupPath
Debug.Print “Auto-Backup Success: ” & backupPath
Exit Sub

ErrHandler:
Debug.Print “Backup Failed: ” & Err.Description
End Sub

‘ — 重要:ライフサイクル管理 —
‘ プレゼンテーションが閉じられたらタイマーを止める
Private Sub App_PresentationClose(ByVal Pres As Presentation)
If PowerPoint.Presentations.Count <= 1 Then StopEngine End If End Sub Private Sub Class_Terminate() StopEngine End Sub

2.3. 起動用モジュール(ThisPresentation等)

最後に、このエンジンを実体化させる。

‘ 標準モジュール等から呼び出す
Public Sub Run_AutoBackupSystem()
Set BackupEngine = New clsBackupTimer
‘ 300秒(5分)間隔で実行
BackupEngine.StartEngine 300
End Sub

Public Sub Terminate_AutoBackupSystem()
If Not BackupEngine Is Nothing Then
BackupEngine.StopEngine
Set BackupEngine = Nothing
End If
End Sub

3. プロフェッショナルの視点:なぜこの設計が「強い」のか

① AddressOf と関数の配置

APIのコールバックに使う関数は、必ず標準モジュールになければならない。しかし、ロジックを標準モジュールに書き散らすのは保守性を下げる。そこで、`TimerProc`は単なる「ゲート(入り口)」として機能させ、実処理は`clsBackupTimer`というオブジェクトに委譲している。これにより、状態管理(インターバル時間やフラグ)をオブジェクト内にカプセル化できる。

② 編集モードへの配慮

PowerPoint VBAの最大の弱点は、ユーザーがスライド内のテキストボックスを編集(IME入力中など)している際にコードが介入すると、非常に不安定になる点だ。
`ExecuteBackup`内で`On Error Resume Next`やエラーハンドリングを厳重に行っているのは、保存処理が競合して「保存できません」というダイアログが出てユーザーの手を止めるのを防ぐためだ。`SaveCopyAs`メソッドは、現在の作業ファイルをロックせずにコピーを作成するため、この用途には最適である。

③ メモリリークと強制終了の回避

`SetTimer`で確保したリソースは、必ず`KillTimer`で解放しなければならない。もし解放せずにVBAプロジェクトをリセットしたりファイルを閉じたりすると、Windows OS側は依然として存在しないVBAのメモリアドレスを呼び出し続け、PowerPointは即座に強制終了(アボート)する。
クラスモジュールの`Class_Terminate`および`App_PresentationClose`イベントで確実に`StopEngine`を叩く構造は、プロフェッショナルなツールにおける「最低限の嗜み」である。

結言:自動化の先にある「信頼性」

PowerPoint VBAに`OnTime`がないのは制限ではない。Windows APIという広大な宇宙へアクセスする「招待状」だと捉えるべきだ。

今回紹介した「APIタイマー + クラスイベント」のアーキテクチャは、バックアップだけでなく、リアルタイムなダッシュボード更新や、一定時間操作がない場合の自動ログアウト処理など、応用範囲は無限に広がる。

コードをコピペして動かす段階は卒業しよう。オブジェクトがいつ生まれ、いつ死ぬのか。APIがどこを指しているのか。その流れを掌握した時、あなたの書くVBAは単なるマクロから、堅牢な「アプリケーション」へと進化する。

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