Project VBAを掌握する:クラスモジュールによるタスク操作の「抽象化」と「メモリ戦略」
MS ProjectのVBA開発において、標準モジュールに数千行のプロシージャを並べるのは、技術的負債を自ら発行しているに等しい。特にProjectのオブジェクトモデルは、`Task`や`Resource`といった実体に対し、イベントハンドリングや複雑なロジックが絡み合うと、一気にスパゲッティ化する。
本稿では、Project VBAを「単なるスクリプト」から「堅牢なシステム」へと昇華させるための、クラスモジュール活用術とメモリ管理の深淵に踏み込む。
—
1. なぜ「タスク・ラッパー」が必要なのか
MS Projectの`Task`オブジェクトを直接操作すると、参照の迷路に陥る。例えば、特定のカスタムフィールドを更新し、進捗を計算し、ログを吐くという一連の流れを標準モジュールに書くと、修正時に地獄を見る。
ここで導入すべきは「TaskWrapperクラス」だ。タスクのライフサイクルと操作をカプセル化し、呼び出し側には「何をしたいか」という意図のみを伝えさせる。
TaskWrapperクラスの設計指針
‘ クラス名: clsTaskWrapper
Option Explicit
Private m_Task As MSProject.Task
‘ コンストラクタ代わりの初期化メソッド
Public Sub Initialize(ByRef TargetTask As MSProject.Task)
If TargetTask Is Nothing Then Err.Raise 91, , “タスクが割り当てられていません”
Set m_Task = TargetTask
End Sub
‘ 複雑なロジックをカプセル化
Public Sub UpdateProgress(ByVal PercentComplete As Long)
‘ Windows APIによるログ記録やシステム連携などをここに集約可能
m_Task.PercentComplete = PercentComplete
‘ ここで独自のバリデーションや例外処理を完結させる
End Sub
‘ クラス終了時のクリーンアップ
Private Sub Class_Terminate()
Set m_Task = Nothing
End Sub
2. メモリ最適化とオブジェクト解放の極意
VBAにおいて`Set obj = Nothing`を怠ることは、単なる行儀の問題ではない。ProjectのCOMコンポーネントは、循環参照や解放の遅延によってメモリリークを引き起こしやすく、長時間稼働するタスクスケジューラや大規模工程表の自動更新では致命的となる。
ガベージコレクションを「手動」で制御する
Project VBAでは、`Application.ActiveProject`を安易に多用してはならない。プロジェクトオブジェクトは一度変数に格納し、処理の終端で確実に解放する。
Public Sub MasterProcess()
Dim prj As MSProject.Project
Dim tsk As MSProject.Task
Dim wrapper As clsTaskWrapper
Set prj = Application.ActiveProject
‘ オブジェクトの明示的生成と解放
For Each tsk In prj.Tasks
If Not tsk Is Nothing Then
Set wrapper = New clsTaskWrapper
wrapper.Initialize tsk
wrapper.UpdateProgress 100
‘ 即座に解放し、スタックを軽くする
Set wrapper = Nothing
End If
Next tsk
Set prj = Nothing
End Sub
3. Windows APIによるシステム間連携の強化
Project VBA単体では突破できない壁がある。例えば、外部サーバーからのリアルタイムな進捗取得や、高精度なタスク実行ログの排他制御だ。ここでは`Kernel32.dll`を直接叩くことで、VBAの制約を無効化する。
Mutexによる排他制御(サンプル)
複数のProjectインスタンスが同時にログファイルへ書き込むのを防ぐ必要がある場合、APIの力を借りる。
If VBA7 Then
Private Declare PtrSafe Function CreateMutex Lib “kernel32” Alias “CreateMutexA” ( _
ByVal lpMutexAttributes As Long, ByVal bInitialOwner As Long, ByVal lpName As String) As LongPtr
Private Declare PtrSafe Function ReleaseMutex Lib “kernel32” (ByVal hMutex As LongPtr) As Long
End If
‘ 複雑なシステム間連携の際は、このようなAPIラッパーを
‘ 別途「clsSystemLock」として切り出すのがシニアの流儀である
4. 伝説的アーキテクトからの提言
Project VBAで大規模な自動化を組む際、最も重要なのは「標準オブジェクトを信頼しすぎないこと」だ。
1. カプセル化の徹底: `Task`オブジェクトの生データを外部に晒さない。すべてクラスを通じてアクセスさせる。
2. イベントの監視: `Project_BeforeTaskChange`などのイベントとクラスモジュールを組み合わせ、タスクが変更された瞬間にバリデーションを行うアーキテクチャを構築せよ。
3. エラーハンドリングの共通化: すべてのクラスでエラーコードを標準化し、一箇所でログ(またはDBへのパケット送信)を拾えるようにする。
VBAはレガシーではない。使い方次第で、現代のクラウドネイティブな環境とも互角に渡り合える強力なグルー(接着剤)になる。コードを量産するのではなく、オブジェクトの寿命を管理し、システムの整合性を保つこと。それこそが、現場を掌握するエンジニアの唯一の道である。
—
追伸:パフォーマンスチューニングの際は、必ず `Application.ScreenUpdating = False` を忘れないこと。これが物理的な速度を決定づける最重要の定石だ。
