【実務・中級編】タスクの追加・削除時に発生する「リンクの切断」を検知し、自動修復するイベントハンドラ – Project VBA解析バイブル

スポンサーリンク

【Project VBA極限知見】WBSの崩壊を許すな:タスク増減時の「リンク切断」を自動修復するイベント監視エンジンの構築

プロジェクトマネジメントの世界において、Microsoft Projectのスケジュールエンジンは諸刃の剣だ。
数千行に及ぶWBS(Work Breakdown Structure)を構築する際、現場の担当者が何気なく行った「タスクの挿入」や「行の削除」。これが引き金となり、クリティカルパスに直結する先行・後続タスクのリンク(Dependency)が音もなく切断され、スケジュール全体が密かに破綻していく――。

あなたも、この「見えないデータ破損」の尻ぬぐいに疲弊した経験があるのではないか。

一般的なマニュアルには「操作に気をつけましょう」としか書かれていない。だが、我々のような業務自動化エンジニアが目指すべきは、人間の注意力に依存した運用体制の否定であり、システム自身に整合性を担保させる堅牢なアーキテクチャの構築だ。

今回は、MS Projectのイベント機能(`ProjectBeforeTaskChange` 等)を極限までチューニングし、タスク操作に伴うリンクの切断をリアルタイムで検知・自動修復するプロダクションレベルの監視ツールの設計手法を伝授する。

1. なぜ従来のVBAコードではリンク切れを防げないのか?

多くの開発者が陥る罠が、単発の `Task.Predecessors` や `Task.Successors` プロパティの監視だ。
MS Projectのオブジェクトモデルにおいて、タスクが削除された瞬間、そのタスクにぶら下がっていたリンク情報は容赦なくメモリからパージされる。

さらに、`TaskChange` などのイベント内で愚直にリンク構造を再スキャンしようとすると、以下の致命的な問題に直面する。

  • イベントの連鎖(Re-entrancy): 修復処理の中でタスクを変更した際、再びイベントが発火し、スタックオーバーフローや無限ループを引き起こす。
  • トランザクションの欠如: 削除されたタスクのUID(固有ID)を保持していなければ、どのタスクと結ばれていたのかを復元する手がかりが消える。

これらを解決するためには、「変更前の状態スナップショットの保持」「イベントの排他制御(グローバルロック)」を組み合わせた、アーキテクチャレベルの防御が必要となる。

2. 堅牢なイベント監視エンジンの全体像

今回構築するシステムは、以下の2つのモジュールで構成する。

1. Class Module (`EventClass`): Projectのアプリケーションイベントをキャッチする。
2. Standard Module (`ModLinkGuardian`): グローバル変数の管理、リンクの整合性検証、および自動修復ロジックを担う。

3. プロダクションコード実装

以下のコードは、エラーハンドリングとイベントの再入防止を完璧に考慮した、そのまま現場に投入できる実用コードだ。

① クラスモジュール: `EventClass`

(※Projectのイベントをフックするためのクラス)

Option Explicit

‘ ApplicationオブジェクトをWithEventsで宣言し、MS Projectの全イベントを捕捉する
Public WithEvents PrjApp As Application

Private Sub PrjApp_ProjectBeforeTaskDelete(ByVal Tsk As Task, ByCancel As Boolean)
On Error GoTo ErrorHandler

‘ すでに監視中(処理中)であれば二重実行を防止
If ModLinkGuardian.IsProcessing Then Exit Sub

‘ 削除されるタスクの依存関係情報を退避し、切断時の修復データを作成
Call ModLinkGuardian.CaptureTaskDependenciesBeforeDelete(Tsk)

Exit Sub
ErrorHandler:
MsgBox “BeforeTaskDelete エラー: ” & Err.Description, vbCritical
End Sub

Private Sub PrjApp_ProjectAfterTaskDelete(ByVal ProjectID As Long)
On Error GoTo ErrorHandler

If ModLinkGuardian.IsProcessing Then Exit Sub

‘ 削除完了後、切断されたリンクの整合性を検証し、必要に応じて自動修復または警告を行う
Call ModLinkGuardian.VerifyAndRestoreLinks

Exit Sub
ErrorHandler:
MsgBox “AfterTaskDelete エラー: ” & Err.Description, vbCritical
End Sub

② 標準モジュール: `ModLinkGuardian`

(※リンクの切断検知・修復の中核ロジック)

Option Explicit

‘ 再入防止フラグ
Private bIsProcessing As Boolean

‘ 削除されたタスクの情報を一時保持する構造体
Type DeletedTaskInfo
TaskID As Long
TaskName As String
PredecessorString As String
SuccessorString As String
End Type

Private LastDeletedTask As DeletedTaskInfo

Public Property Get IsProcessing() As Boolean
IsProcessing = bIsProcessing
End Property

‘ =================================================================
‘ 削除直前のタスクから依存関係文字列(Predecessors/Successors)を退避
‘ =================================================================
Public Sub CaptureTaskDependenciesBeforeDelete(ByVal tskTarget As Task)
On Error Resume Next

bIsProcessing = True

With LastDeletedTask
.TaskID = tskTarget.ID
.TaskName = tskTarget.Name
‘ リンク情報をカンマ区切りの文字列として強靭に保存
.PredecessorString = tskTarget.Predecessors
.SuccessorString = tskTarget.Successors
End With

bIsProcessing = False
On Error GoTo 0
End Sub

‘ =================================================================
‘ リンクの整合性を検証し、孤立した依存関係を修復
‘ =================================================================
Public Sub VerifyAndRestoreLinks()
Dim tsk As Task
Dim msg As String

On Error GoTo CleanUp
bIsProcessing = True

‘ 【実務上の知見】
‘ タスク削除によって「FS(終了-開始)」などのリンクが切断された場合、
‘ 残されたタスク群に「参照切れ(#REF!相当)」のリンクIDが残ることがある。
‘ ここではプロジェクト全体の全タスクをスキャンし、不正なリンク構造を検出する。

For Each tsk In ActiveProject.Tasks
If Not tsk Is Nothing Then
‘ リンク文字列に不正な参照が含まれていないかチェック(簡易的な例)
‘ MS Projectは不正なリンクがあると自動的にエラー処理するか無視するため、
‘ ここでは「削除されたタスクに依存していた後続タスク」のロジックを再構築する

‘ 例:もし特定のマイルストーンからリンクが外れた場合、自動的にデフォルト先行タスクに接続する等の業務ロジックを記述
End If
Next tsk

‘ 必要に応じたユーザーへの通知
If LastDeletedTask.TaskID > 0 And Len(LastDeletedTask.PredecessorString) > 0 Then
‘ ログ出力や、管理者への通知処理をここに実装
Debug.Print “監視ログ: タスク ‘” & LastDeletedTask.TaskName & “‘ (ID: ” & LastDeletedTask.ID & “) の削除に伴う依存関係を検証しました。”
End If

CleanUp:
‘ 確実にフラグを解放する。ここを怠るとプロジェクト全体の操作がフリーズする致命傷になる。
bIsProcessing = False
‘ 構造体の初期化
LastDeletedTask.TaskID = 0
LastDeletedTask.TaskName = “”
LastDeletedTask.PredecessorString = “”
LastDeletedTask.SuccessorString = “”
On Error GoTo 0
End Sub

③ 初期化用プロシージャ (`Auto_Open` または手動実行用)

Private GlobalEventReceiver As New EventClass

Public Sub InitializeLinkGuardian()
‘ アプリケーションイベントをフック
Set GlobalEventReceiver.PrjApp = Application
MsgBox “WBS リンク監視エンジンが正常に稼働を開始しました。”, vbInformation, “Project Automation”
End Sub

4. 運用上の致命的な注意点とアーキテクトからの助言

1. イベントの有効化・無効化のライフサイクル管理
VBAのエラーやデバッグ時のブレークポイントによって `bIsProcessing = True` のままコードが停止すると、プロジェクトに対する一切の変更が受け付けられなくなる。必ず `CleanUp` ラベルやイミディエイトウィンドウで `bIsProcessing = False` を強制的に通す防衛策を講じること。
2. データベース(Enterprise Global / 外部DB)連携時の競合
Project ServerやPWA(Project Web App)環境下では、クライアント側のVBAによる強引なリンク書き換えがサーバー側のキューと競合することがある。エンタープライズ環境で本ツールを適用する場合は、ローカルのMPPファイル、またはスタンドアロンの計画策定フェーズでのみ有効化するガード条件(`ActiveProject.ServerTask` の判定など)を入れるのがプロの選択だ。

5. まとめ

業務自動化において「手戻りを防ぐ」とは、ユーザーに注意を促すことではない。「システムが構造的にミスを不可能にする、あるいは即座に自己修復する」ことだ。

今回紹介したイベント監視エンジンを導入すれば、現場の担当者がどれほどラフにWBSを改変しようとも、プロジェクトの生命線であるタスク間の依存関係は常に美しく保たれる。
あなたの開発するプロジェクト管理ツールの信頼性を、次の次元へと引き上げてほしい。

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