【テクニカル・上級編】【上級者向け】カスタムフィールドの依存関係を可視化する依存グラフ生成ツール – Project VBA解析バイブル

スポンサーリンク
スポンサーリンク

【極限の知見】MS Project VBAにおけるカスタムフィールド依存関係の解析とグラフ構造化

大規模なエンタープライズ・スケジューリングにおいて、Microsoft Projectのカスタムフィールドは「神の領域」であると同時に、システムの「脆弱性の温床」でもある。

`Text1` に入力された複雑な数式が、`Number5` を参照し、それがさらに `Resource1` の計算に波及する――。この不可視の依存関係(Dependency Chain)を把握せずに数式を書き換える行為は、地雷原を地図なしで歩くことに等しい。数式の一箇所での変更が、プロジェクト全体の計算ロジックを崩壊させ、静かなるデータ汚染を引き起こす。

本稿では、シニアエンジニアおよびシステム管理者が直面するこの「数式のエントロピー増大」に対し、カスタムフィールドの依存関係を抽出し、グラフ構造として可視化するための、極めて実践的かつ低レイヤーなアプローチを解説する。

1. アーキテクチャの設計思想:メタデータの抽出とパース

カスタムフィールドの依存関係を解析するためには、単に `Task.CustomFields` を走査するだけでは不十分だ。我々が解析すべき対象は、`Application.CustomFields` オブジェクトに格納されている `Formula` プロパティ、すなわち「数式そのもの」である。

設計の肝は以下の3層構造にある。

1. Extraction Layer: `Application.CustomFields` から各フィールドの定義(名前、型、数式)を抽出する。
2. Parsing Layer: 正規表現(`VBScript.RegExp`)を用い、数式文字列内から `[Field]` 形式のパターンを特定する。
3. Graph Layer: 抽出した依存関係を「隣接リスト(Adjacency List)」として保持し、グラフ構造を構築する。

ここで重要なのは、オブジェクトのライフサイクル管理だ。VBAはCOM(Component Object Model)の上で動いている。大量のカスタムフィールドをループで処理する際、参照カウントの管理を誤れば、MS Projectプロセスそのものがメモリリークを起こし、最悪の場合、不可解なクラッシュを招く。

2. 実装:依存関係解析エンジン

以下に、依存関係を解析し、結果を解析可能な形式(CSV/テキスト)で出力するコアロジックを示す。ここでは、パフォーマンスとメモリ効率を考慮し、`Scripting.Dictionary` を用いてグラフを構築する。

Option Explicit

' Windows APIを利用した高精度な時間計測(プロファイリング用)
' パフォーマンスのボトルネックを特定するために不可欠

If VBA7 Then

Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long

Else

Declare Function GetTickCount Lib "kernel32" () As Long

End If

''' <summary> ''' カスタムフィールドの依存関係を解析するメインクラス(モジュール) ''' </summary> Public Sub AnalyzeCustomFieldDependencies() Dim startTime As Long startTime = GetTickCount() Dim app As MSProject.Application Set app = MSProject.Application ' 解析結果を格納するDictionary (Key: 親フィールド, Value: 子フィールドのCollection) ' Scripting.Dictionaryはメモリ効率と検索速度のバランスに優れる Dim graph As Object Set graph = CreateObject("Scripting.Dictionary") ' 正規表現オブジェクトの生成 Dim regEx As Object Set regEx = CreateObject("VBScript.RegExp") With regEx .Pattern = "\[(.*?)\]" ' [Field] 形式をキャプチャするパターン .Global = True .IgnoreCase = True End With Dim cf As MSProject.CustomField Dim formula As String Dim matches As Object Dim match As Object Dim targetField As String On Error GoTo ErrorHandler ' 1. カスタムフィールドの走査 ' Application.CustomFields は全カスタムフィールドの集合体である For Each cf In app.CustomFields formula = cf.Formula ' 数式が空でない場合のみ解析 If Len(Trim(formula)) > 0 Then ' グラフのノード(親)を初期化 If Not graph.Exists(cf.Name) Then Set graph(cf.Name) = CreateObject("Scripting.Dictionary") End If ' 2. 正規表現によるパース Set matches = regEx.Execute(formula) For Each match In matches ' match.SubMatches(0) で括弧内の文字列 [Field] を抽出 targetField = match.SubMatches(0) ' 依存先のフィールドを登録 If Not graph(cf.Name).Exists(targetField) Then graph(cf.Name).Add targetField, True End If Next match End If Next cf ' 3. 結果の出力 (デバッグコンソールおよびファイルへの書き出し) OutputGraph graph ' クリーンアップ: 明示的なオブジェクト解放 ' COMオブジェクトのライフサイクルを制御し、メモリリークを防止する Set regEx = Nothing Set graph = Nothing Set app = Nothing Debug.Print "Analysis completed in " & (GetTickCount() - startTime) & " ms." MsgBox "解析が完了しました。", vbInformation Exit Sub ErrorHandler: MsgBox "致命的なエラーが発生しました: " & Err.Description, vbCritical ' エラー時もリソースを解放する責務がある Set regEx = Nothing Set graph = Nothing End Sub ''' <summary> ''' 構築されたグラフ構造をテキスト形式で出力する ''' </summary> Private Sub OutputGraph(ByRef graph As Object) Dim key As Variant Dim childKey As Variant Dim output As String output = "Source,Target" & vbCrLf For Each key In graph.Keys ' 子要素が存在する場合のみエッジを書き出す If graph(key).Count > 0 Then For Each childKey In graph(key).Keys output = output & key & "," & childKey & vbCrLf Next childKey End If Next key ' 標準出力(イミディエイトウィンドウ)へ Debug.Print output ' 必要に応じてFileSystemObjectを用いてファイル保存を行う ' ここでは概念を示すに留める End Sub

3. 深層解説:プロフェッショナルのための最適化テクニック

3.1 COMオブジェクトのメモリ管理

VBAにおいて `Set obj = Nothing` は単なる作法ではない。特に `MSProject.Application` や `Scripting.Dictionary` のような重いCOMオブジェクトを多用する場合、スコープを抜ける前に明示的に解放することは、長時間稼働する自動化サーバーや、大規模なプロジェクトファイルを開き続ける管理者のための「防衛策」である。参照カウントが残存すると、MS Projectのプロセスがバックグラウンドに残り続け、ゾンビプロセス化する原因となる。

3.2 正規表現のコスト

`VBScript.RegExp` は強力だが、ループ内で毎回 `New RegExp` を行うのは愚策だ。本実装のように、ループの外で一度だけインスタンス化し、パターンをプリコンパイル(`.Pattern` の設定)しておくことが、計算量のオーダーを抑える鍵となる。

3.3 依存関係の「循環」への警戒

本ツールは依存関係を抽出するが、実運用においては「循環参照(Circular Reference)」の検知機能を追加すべきだ。`A -> B -> A` というループが存在する場合、Projectの再計算エンジンは無限ループに陥るか、計算結果が不定になる。グラフ理論における「トポロジカルソート」のアルゴリズムを実装することで、循環の有無を数学的に証明することが可能になる。

4. 次なるステップ:可視化の極致へ

本ツールで出力されるCSV形式のデータ(`Source,Target`)は、そのまま Graphviz (dot言語) や PythonのNetworkX に流し込むことができる。

// Graphviz形式の出力例
digraph G {
  "Text1" -> "Number1";
  "Number1" -> "Text2";
  "Text2" -> "Text1"; // 循環参照の検知
}

このように、VBAを「解析エンジン」として使い、可視化を「外部の専門ツール」に委ねる。これこそが、レガシーな環境を現代的なアーキテクチャへと昇華させる、真のエンジニアリングである。

複雑怪奇な数式の迷宮を、論理の光で解き明かせ。

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