【テクニカル・上級編】【初心者向け】ベースライン設定時に「設定日時」をカスタムフィールドへ自動書き込みする仕組み – Project VBA解析バイブル

スポンサーリンク

【Project VBA極限解説】ベースライン設定日時のカスタムフィールド自動書き込みとオブジェクトライフサイクル管理

Project VBA(MS ProjectのVBA環境)の現場において、プロジェクトの進捗管理の生命線となるのが「ベースライン」である。計画値と実績値を比較するためになくてはならない機能だが、MS Project標準のUI操作だけでは、「いつ、どのタスクのベースラインが設定されたか」を視覚的かつ詳細にトレーサビリティを確保しながら保持し続けることは困難だ。

特に、複数回の計画変更(リプランニング)が発生するエンタープライズ環境では、タスク個別のベースライン設定日時をカスタムフィールドへ動的に焼き付ける仕組みが求められる。

本稿では、単なるコードの断片の提示にとどまらず、MS Projectのオブジェクトモデルの挙動、メモリ最適化、そして実運用で耐えうる堅牢なイベント駆動型アーキテクチャについて、チーフアーキテクトの視点から極限まで解説する。

1. MS Project VBAにおけるアーキテクチャの罠とオブジェクト管理

MS ProjectのVBA開発において、Excel VBAとの最大の違いは「タスクとリソースの階層構造(CollectionとTask/Resourceオブジェクト)の重み」にある。数千行を超える大規模工程表において、不適切なオブジェクト参照の保持や解放漏れは、即座にCOMコンポーネントのメモリリーク、最悪の場合はMS Project自体の強制終了を引き起こす。

オブジェクトの明示的解放(The Right Way)

VBAのガベージコレクションは頼りにならない。特に`Application.ActiveProject.Tasks`のような重厚なコレクションをループさせる際は、変数への代入と明示的な`Nothing`代入による参照カウントのデクリメントが鉄則である。

‘ 良い例:オブジェクト変数を局所化し、確実に解放する
Dim tsk As Task
Dim tasksColl As Tasks
Set tasksColl = ActiveProject.Tasks

Dim i As Long
For i = 1 to tasksColl.Count
Set tsk = tasksColl(i)
If Not tsk Is Nothing Then
‘ 処理ロジック
End If
Set tsk = Nothing ‘ ループごとに確実に解放
Next i
Set tasksColl = Nothing

この泥臭いまでのメモリ管理の意識こそが、レガシーなVBA環境を何年もの間、無停止で稼働させ続ける唯一の防壁となる。

2. ベースライン設定検知のメカニズムとカスタムフィールドの選定

MS Projectには、Excelの`Worksheet_Change`のような「ベースライン設定時」に直接発火するネイティブなイベント(例: `BaselineSaved`のような専用イベント)は存在しない。

そのため、実務においては以下のいずれかの手法を選択する。
1. リボンやメニューのカスタムUI(Callback)からマクロを叩く
2. プロジェクトの保存イベント(`ProjectBeforeSave`)や特定のカスタムプロパティ変更をフックする

今回は、最も堅牢かつユーザーの運用ミスを防ぐ「カスタムマクロによるベースライン設定のラップ(Wrapper)」と、その実行時にタスク単位で日時をカスタムフィールド(例:`Text1` または日付型の `Date1`)へ書き込むアプローチを採用する。

使用するカスタムフィールドの設計

  • フィールド名: `Text30` (または `Date1`。プロジェクト標準のユーザー定義フィールドを使用)
  • 用途: ベースラインが設定されたタイムスタンプ(`YYYY/MM/DD HH:NN:SS`)を保持。

3. 実装コード:ベースライン自動スタンプ・エンジン

以下のコードは、アクティブプロジェクトの全タスク(または選択されたタスク)に対し、ベースライン(Baseline 0)を設定しつつ、その瞬間の正確な日時をカスタムフィールドへ書き込む実用モジュールである。

エラーハンドリング、トランザクション的な処理、そしてパフォーマンスを極限まで高めた設計となっている。

‘ ==============================================================================
‘ Module: m_BaselineManager
‘ Description: ベースライン設定とカスタムフィールドへのタイムスタンプ自動書き込み
‘ ==============================================================================
Option Explicit

Public Sub SetBaselineWithTimestamp()
Dim prj As Project
Set prj = ActiveProject

‘ プロジェクトが開かれていない場合のガード
If prj Is Nothing Then
MsgBox “有効なプロジェクトが開かれていません。”, vbCritical, “致命的エラー”
Exit Sub
End If

On Error GoTo ErrorHandler

‘ パフォーマンス最適化:画面描画と自動再計算の停止
Application.ScreenUpdating = False
Application.Calculation = pjManual

Dim currentTimestamp As String
currentTimestamp = Format(Now, “yyyy/mm/dd hh:nn:ss”)

Dim tsk As Task
Dim targetTasks As Tasks
Dim countUpdated As Long
countUpdated = 0

‘ 選択されたタスクがある場合は選択範囲のみ、なければ全体を対象とする
‘ 実務では全タスク対象が多いため、今回は全タスクを走査
Set targetTasks = prj.Tasks

Dim i As Long
Dim totalTasks As Long
totalTasks = targetTasks.Count

For i = 1 To totalTasks
Set tsk = targetTasks(i)

‘ サマリータスクや削除済みタスクを除外
If Not tsk Is Nothing Then
If Not tsk.Summary Then
‘ MS Projectの標準機能でベースライン0を設定(必要に応じて個別設定も可)
‘ ※今回はプロジェクト全体またはタスク単位の疑似設定としてText30に記録

‘ カスタムフィールド (Text30 = ユーザー定義テキスト10等) に日時を書き込み
‘ ※環境に合わせて Text30 や Date1 に変更してください
tsk.Text30 = currentTimestamp

countUpdated = countUpdated + 1
End If
End If
Set tsk = Nothing ‘ メモリ解放
Next i

‘ 実際にMS Projectのベースライン保存コマンドを実行
‘ 引数: Set Baseline, All tasks, From scratch
ChartBaselineSave prj

‘ 正常終了処理
Application.Calculation = pjAutomatic
Application.ScreenUpdating = True

MsgBox “ベースラインの記録が完了しました。” & vbCrLf & _
“更新タスク数: ” & countUpdated & vbCrLf & _
“記録日時: ” & currentTimestamp, vbInformation, “完了”

Exit Sub

ErrorHandler:
‘ 異常終了時の環境復旧
Application.Calculation = pjAutomatic
Application.ScreenUpdating = True

MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“Error # ” & Err.Number & “: ” & Err.Description, vbCritical, “システムエラー”

If Not tsk Is Nothing Then Set tsk = Nothing
Set targetTasks = Nothing
Set prj = Nothing
End Sub

‘ ——————————————————————————
‘ Private Helper: MS Projectの標準ベースライン保存APIのラップ
‘ ——————————————————————————
Private Sub ChartBaselineSave(ByRef targetPrj As Project)
‘ MS Project内部のベースラインダイアログと同等の処理をコードから実行
‘ Baseline 0 を設定する
targetPrj.SetBaseline Baseline:=pjBaseline
End Sub

4. チーフアーキテクトが教える現場の知見:運用上の注意点

この仕組みを実際の社内システムや大規模プロジェクト管理に導入する際、以下の「現場の罠」に直面することが多々ある。あらかじめ対策を講じておくこと。

1. ガントチャートビューでの視覚化(Gantt Chart Formatting)

カスタムフィールド(`Text30`)に書き込まれた日時をただ保持するだけでは意味がない。これをプロジェクトマネージャーやメンバーに視覚的に認知させるため、「ガントチャートのバーの横にベースライン設定日時を表示する」、あるいは条件付き書式やカスタムビューとの連携を行うべきだ。
フィールドの値が更新されたタスクに対し、視覚的なインジケータ(Graphical Indicators)を設定することで、「このタスクはいつの計画変更の影響を受けたか」が一目で把握できるようになる。

2. 複数回のリプランニングへの対応

ベースラインが上書きされるたびに `Text30` が最新の日時に書き換わるため、「前回のベースラインはいつだったか」の履歴が消えることになる。
もし完全な履歴管理(監査証跡)が必要な場合は、単一のカスタムフィールドへの上書きではなく、外部データベース(SQL ServerやSharePoint Lists等)へADO/REST API経由でタスクIDとタイムスタンプを非同期送信するアーキテクチャへの拡張を検討すべきである。

結び

Project VBAの制御は、他のOffice製品のVBAに比べてドキュメントが少なく、独自の作法が要求される。しかし、オブジェクトのライフサイクルを完全に掌握し、適切なメモリ管理とエラーハンドリングを施したコードベースは、エンタープライズの現場において極めて高い信頼性を発揮する。

本稿で示したベースライン日時自動書き込みの仕組みは、その第一歩に過ぎない。ここからさらに高度なシステム間連携や自動化パイプラインへと昇華させ、真に価値のあるプロジェクト管理基盤を構築してほしい。

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