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

スポンサーリンク

PowerPoint VBAの限界を突破せよ:Windows APIで構築する「堅牢な」バックグラウンド・オートセーバー

PowerPoint VBA開発において、多くのエンジニアが最初に直面する壁。それは「Excelの`Application.OnTime`のような非同期タイマーが存在しない」という絶望的な事実です。

プレゼンテーションの編集作業中、不意のクラッシュで数時間の苦労が水泡に帰した経験はありませんか? 「自動保存があるから大丈夫」という甘えは、業務自動化を志すエンジニアが最も排除すべき脆弱性です。

今日は、Windows APIの`SetTimer`とクラスモジュールの`WithEvents`を組み合わせ、PowerPointの心臓部に強引に「定期実行エンジン」を組み込む極限のアーキテクチャを伝授します。

なぜ「ループ処理」ではいけないのか

初心者がやりがちな「`DoEvents`を挟んだ無限ループ」は、CPUを不必要に占有し、PowerPointのUIスレッドを重くします。これは自動化ではなく「業務阻害」です。

我々が目指すべきは、OSレベルでのイベント駆動です。Windows APIの`SetTimer`を使用することで、PowerPointのメインスレッドを解放しつつ、指定ミリ秒後にOSからVBAへ通知を送る「非同期割り込み」を実現します。

実装アーキテクチャ:3つの構成要素

1. クラスモジュール (`clsTimer`): APIコールバックをラップし、イベントを発生させる。
2. 標準モジュール (`modEngine`): タイマーの起動・停止、バックアップロジックを制御。
3. AppEventクラス: PowerPointのイベント(保存や終了)を監視し、タイマーのライフサイクルを管理。

1. クラスモジュール:`clsTimer`

このクラスはAPIの窓口です。ここを疎結合にしておくことで、他のツールへの流用も容易になります。

‘ クラス名: clsTimer
Option Explicit

Private Declare PtrSafe Function SetTimer Lib “user32” (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As LongPtr
Private Declare PtrSafe Function KillTimer Lib “user32” (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr) As Long

Public Event Tick()
Private m_TimerID As LongPtr

Public Sub StartTimer(IntervalMs As Long)
If m_TimerID <> 0 Then StopTimer
m_TimerID = SetTimer(0, 0, IntervalMs, AddressOf TimerProc)
‘ 外部からインスタンスを特定するためのグローバル保持(後述のProc用)
SaveInstancePtr Me
End Sub

Public Sub StopTimer()
If m_TimerID <> 0 Then
KillTimer 0, m_TimerID
m_TimerID = 0
End If
End Sub

Public Sub RaiseTick()
RaiseEvent Tick
End Sub

2. 標準モジュール:`modEngine`

タイマーが呼び出すコールバック関数は、標準モジュールに配置する必要があります。

‘ 標準モジュール: modEngine
Option Explicit

Private m_Instance As clsTimer

‘ APIから呼び出されるプロシージャ
Public Sub TimerProc(ByVal hwnd As LongPtr, ByVal uMsg As Long, ByVal idEvent As LongPtr, ByVal dwTime As Long)
If Not m_Instance Is Nothing Then m_Instance.RaiseTick
End Sub

Public Sub SaveInstancePtr(obj As clsTimer)
Set m_Instance = obj
End Sub

‘ 実際のバックアップ処理
Public Sub PerformBackup()
On Error Resume Next ‘ 保存失敗で止まらないようにする
Dim pPath As String: pPath = ActivePresentation.Path
If pPath = “” Then Exit Sub ‘ 未保存ファイルは対象外

Dim backupName As String
backupName = pPath & “\Backup_” & Format(Now, “HHmm”) & “_” & ActivePresentation.Name
ActivePresentation.SaveCopyAs backupName
End Sub

堅牢な設計のための「ライフサイクル管理」

単に動くだけではプロとは呼べません。「PowerPointを終了した瞬間にタイマーがゾンビ化してクラッシュする」という悲劇を避けるため、`Application`オブジェクトの`AppEvents`を監視し、終了時に確実に`KillTimer`を叩く必要があります。

‘ クラスモジュール: AppEvents
Public WithEvents App As Application

Private Sub App_PresentationBeforeClose(ByVal Pres As Presentation, Cancel As Boolean)
‘ 終了時に確実にタイマーを殺す
StopTimer ‘ 前述のclsTimerのメソッドを呼び出す
End Sub

プロフェッショナルの注意点:パフォーマンスと安定性

1. 保存先の選定: ネットワークドライブへの保存は避けてください。同期ラグによりUIがフリーズするリスクがあります。ローカルのTempフォルダ等に一度保存し、非同期で移動させるのがベストプラクティスです。
2. エラーハンドリング: `On Error Resume Next`を乱用してはいけません。特に`SaveCopyAs`はファイルロックの競合が起こりやすいため、`Err.Number`を確認してログを吐く仕組みを必ず入れてください。
3. UIへの配慮: 10分ごとのバックアップ時に「保存しました」というメッセージボックスを出すのは最悪のUXです。ステータスバーへの出力(`Application.StatusBar = “Backup finished”`)に留めるのが、熟練者の作法です。

まとめ:あなたの武器として

今回紹介した「Windows APIタイマー × VBAイベント」の構成は、一度組めばどんなPowerPoint業務にも応用できる汎用的なエンジンです。

「なぜ動かないのか」と悩む時間を減らし、「どう拡張するか」を考える時間を増やす。これが、自動化エンジニアの本来の姿です。さあ、このコードをあなたの武器にして、泥臭い手作業からチームを解放してください。


追記:このコードは64bit環境を前提にしています。32bit環境で運用する場合は`LongPtr`を`Long`に読み替えてください。設計思想を理解していれば、それすら自明の理であるはずです。

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